create interface for oci client

Signed-off-by: Meredith Lancaster <malancas@github.com>
This commit is contained in:
Meredith Lancaster 2024-03-12 16:12:45 -06:00
parent be6b042039
commit f055517baa
12 changed files with 110 additions and 71 deletions

View file

@ -51,7 +51,7 @@ func normalizeReference(reference string, pathSeparator rune) (normalized string
return filepath.Clean(reference), fileArtifactType, nil
}
func NewDigestedArtifact(client *oci.Client, reference, digestAlg string) (artifact *DigestedArtifact, err error) {
func NewDigestedArtifact(client oci.Client, reference, digestAlg string) (artifact *DigestedArtifact, err error) {
normalized, artifactType, err := normalizeReference(reference, os.PathSeparator)
if err != nil {
return nil, err

View file

@ -7,7 +7,7 @@ import (
"github.com/distribution/reference"
)
func digestContainerImageArtifact(url string, client *oci.Client) (*DigestedArtifact, error) {
func digestContainerImageArtifact(url string, client oci.Client) (*DigestedArtifact, error) {
if client == nil {
return nil, fmt.Errorf("missing OCI client")
}

View file

@ -10,7 +10,7 @@ import (
func TestDigestContainerImageArtifact(t *testing.T) {
expectedDigest := "1234567890abcdef"
client := oci.NewMockClient()
client := oci.MockClient{}
url := "example.com/repo:tag"
digestedArtifact, err := digestContainerImageArtifact(url, client)
require.NoError(t, err)
@ -20,7 +20,7 @@ func TestDigestContainerImageArtifact(t *testing.T) {
}
func TestParseImageRefFailure(t *testing.T) {
client := oci.NewReferenceFailClient()
client := oci.ReferenceFailClient{}
url := "example.com/repo:tag"
_, err := digestContainerImageArtifact(url, client)
require.Error(t, err)
@ -29,17 +29,17 @@ func TestParseImageRefFailure(t *testing.T) {
func TestFetchImageFailure(t *testing.T) {
testcase := []struct {
name string
client *oci.Client
client oci.Client
expectedErr error
}{
{
name: "Fail to authorize with registry",
client: oci.NewAuthFailClient(),
client: oci.AuthFailClient{},
expectedErr: oci.ErrRegistryAuthz,
},
{
name: "Fail to fetch image due to denial",
client: oci.NewDeniedClient(),
client: oci.DeniedClient{},
expectedErr: oci.ErrDenied,
},
}

View file

@ -0,0 +1,35 @@
package oci
import (
"fmt"
"github.com/google/go-containerregistry/pkg/v1"
)
type MockClient struct {}
func (c MockClient) GetImageDigest(imgName string) (*v1.Hash, error) {
return &v1.Hash{
Hex: "1234567890abcdef",
Algorithm: "sha256",
}, nil
}
type ReferenceFailClient struct {}
func (c ReferenceFailClient) GetImageDigest(imgName string) (*v1.Hash, error) {
return nil, fmt.Errorf("failed to parse reference")
}
type AuthFailClient struct {}
func (c AuthFailClient) GetImageDigest(imgName string) (*v1.Hash, error) {
return nil, ErrRegistryAuthz
}
type DeniedClient struct {}
func (c DeniedClient) GetImageDigest(imgName string) (*v1.Hash, error) {
return nil, ErrDenied
}

View file

@ -14,9 +14,8 @@ import (
var ErrDenied = errors.New("the provided token was denied access to the requested resource, please check the token's expiration and repository access")
var ErrRegistryAuthz = errors.New("remote registry authorization failed, please authenticate with the registry and try again")
type Client struct {
ParseReference func(string, ...name.Option) (name.Reference, error)
Get func(name.Reference, ...remote.Option) (*remote.Descriptor, error)
type Client interface {
GetImageDigest(imgName string) (*v1.Hash, error)
}
func checkForUnauthorizedOrDeniedErr(err transport.Error) error {
@ -31,8 +30,13 @@ func checkForUnauthorizedOrDeniedErr(err transport.Error) error {
return nil
}
type LiveClient struct {
ParseReference func(string, ...name.Option) (name.Reference, error)
Get func(name.Reference, ...remote.Option) (*remote.Descriptor, error)
}
// where name is formed like ghcr.io/github/my-image-repo
func (c Client) GetImageDigest(imgName string) (*v1.Hash, error) {
func (c LiveClient) GetImageDigest(imgName string) (*v1.Hash, error) {
name, err := c.ParseReference(imgName)
if err != nil {
return nil, fmt.Errorf("failed to create image tag: %w", err)
@ -52,59 +56,59 @@ func (c Client) GetImageDigest(imgName string) (*v1.Hash, error) {
return &desc.Digest, nil
}
func NewLiveClient() *Client {
return &Client{
func NewLiveClient() *LiveClient {
return &LiveClient{
ParseReference: name.ParseReference,
Get: remote.Get,
}
}
func NewMockClient() *Client {
return &Client{
ParseReference: func(string, ...name.Option) (name.Reference, error) {
return name.Tag{}, nil
},
Get: func(name.Reference, ...remote.Option) (*remote.Descriptor, error) {
d := remote.Descriptor{}
d.Digest = v1.Hash{
Hex: "1234567890abcdef",
Algorithm: "sha256",
}
// func NewMockClient() *Client {
// return &Client{
// ParseReference: func(string, ...name.Option) (name.Reference, error) {
// return name.Tag{}, nil
// },
// Get: func(name.Reference, ...remote.Option) (*remote.Descriptor, error) {
// d := remote.Descriptor{}
// d.Digest = v1.Hash{
// Hex: "1234567890abcdef",
// Algorithm: "sha256",
// }
return &d, nil
},
}
}
// return &d, nil
// },
// }
// }
func NewReferenceFailClient() *Client {
return &Client{
ParseReference: func(string, ...name.Option) (name.Reference, error) {
return nil, fmt.Errorf("failed to parse reference")
},
Get: func(name.Reference, ...remote.Option) (*remote.Descriptor, error) {
return nil, nil
},
}
}
// func NewReferenceFailClient() *Client {
// return &Client{
// ParseReference: func(string, ...name.Option) (name.Reference, error) {
// return nil, fmt.Errorf("failed to parse reference")
// },
// Get: func(name.Reference, ...remote.Option) (*remote.Descriptor, error) {
// return nil, nil
// },
// }
// }
func NewAuthFailClient() *Client {
return &Client{
ParseReference: func(string, ...name.Option) (name.Reference, error) {
return name.Tag{}, nil
},
Get: func(name.Reference, ...remote.Option) (*remote.Descriptor, error) {
return nil, &transport.Error{Errors: []transport.Diagnostic{{Code: transport.UnauthorizedErrorCode}}}
},
}
}
// func NewAuthFailClient() *Client {
// return &Client{
// ParseReference: func(string, ...name.Option) (name.Reference, error) {
// return name.Tag{}, nil
// },
// Get: func(name.Reference, ...remote.Option) (*remote.Descriptor, error) {
// return nil, &transport.Error{Errors: []transport.Diagnostic{{Code: transport.UnauthorizedErrorCode}}}
// },
// }
// }
func NewDeniedClient() *Client {
return &Client{
ParseReference: func(string, ...name.Option) (name.Reference, error) {
return name.Tag{}, nil
},
Get: func(name.Reference, ...remote.Option) (*remote.Descriptor, error) {
return nil, &transport.Error{Errors: []transport.Diagnostic{{Code: transport.DeniedErrorCode}}}
},
}
}
// func NewDeniedClient() *Client {
// return &Client{
// ParseReference: func(string, ...name.Option) (name.Reference, error) {
// return name.Tag{}, nil
// },
// Get: func(name.Reference, ...remote.Option) (*remote.Descriptor, error) {
// return nil, &transport.Error{Errors: []transport.Diagnostic{{Code: transport.DeniedErrorCode}}}
// },
// }
// }

View file

@ -22,7 +22,7 @@ func TestRunDownload(t *testing.T) {
baseOpts := Options{
ArtifactPath: "../test/data/sigstore-js-2.1.0.tgz",
APIClient: api.NewTestClient(),
OCIClient: oci.NewMockClient(),
OCIClient: oci.MockClient{},
DigestAlgorithm: "sha512",
OutputPath: tempDir,
Limit: 30,
@ -97,7 +97,7 @@ func TestRunDownload(t *testing.T) {
t.Run("cannot download OCI artifact", func(t *testing.T) {
opts := baseOpts
opts.ArtifactPath = "oci://ghcr.io/github/test"
opts.OCIClient = oci.NewReferenceFailClient()
opts.OCIClient = oci.ReferenceFailClient{}
err := RunDownload(&opts)
require.Error(t, err)
@ -124,7 +124,7 @@ func TestCreateJSONLinesFilePath(t *testing.T) {
defer os.RemoveAll(tempDir)
t.Run("with output path", func(t *testing.T) {
artifact, err := artifact.NewDigestedArtifact(oci.NewMockClient(), "../test/data/sigstore-js-2.1.0.tgz", "sha512")
artifact, err := artifact.NewDigestedArtifact(oci.MockClient{}, "../test/data/sigstore-js-2.1.0.tgz", "sha512")
require.NoError(t, err)
path := createJSONLinesFilePath(artifact.DigestWithAlg(), tempDir)
@ -133,7 +133,7 @@ func TestCreateJSONLinesFilePath(t *testing.T) {
})
t.Run("with nested output path", func(t *testing.T) {
artifact, err := artifact.NewDigestedArtifact(oci.NewMockClient(), "../test/data/sigstore-js-2.1.0.tgz", "sha512")
artifact, err := artifact.NewDigestedArtifact(oci.MockClient{}, "../test/data/sigstore-js-2.1.0.tgz", "sha512")
require.NoError(t, err)
nestedPath := fmt.Sprintf("%s/subdir", tempDir)
@ -144,7 +144,7 @@ func TestCreateJSONLinesFilePath(t *testing.T) {
})
t.Run("with output path with beginning slash", func(t *testing.T) {
artifact, err := artifact.NewDigestedArtifact(oci.NewMockClient(), "../test/data/sigstore-js-2.1.0.tgz", "sha512")
artifact, err := artifact.NewDigestedArtifact(oci.MockClient{}, "../test/data/sigstore-js-2.1.0.tgz", "sha512")
require.NoError(t, err)
nestedPath := fmt.Sprintf("/%s/subdir", tempDir)
@ -155,7 +155,7 @@ func TestCreateJSONLinesFilePath(t *testing.T) {
})
t.Run("without output path", func(t *testing.T) {
artifact, err := artifact.NewDigestedArtifact(oci.NewMockClient(), "../test/data/sigstore-js-2.1.0.tgz", "sha512")
artifact, err := artifact.NewDigestedArtifact(oci.MockClient{}, "../test/data/sigstore-js-2.1.0.tgz", "sha512")
require.NoError(t, err)
path := createJSONLinesFilePath(artifact.DigestWithAlg(), "")

View file

@ -15,7 +15,7 @@ type Options struct {
DigestAlgorithm string
Logger *logging.Logger
Limit int
OCIClient *oci.Client
OCIClient oci.Client
OutputPath string
Owner string
Verbose bool

View file

@ -26,7 +26,7 @@ func TestRunInspect(t *testing.T) {
BundlePath: bundlePath,
DigestAlgorithm: "sha512",
Logger: logging.NewTestLogger(),
OCIClient: oci.NewMockClient(),
OCIClient: oci.MockClient{},
}
t.Run("with valid artifact and bundle", func(t *testing.T) {

View file

@ -18,7 +18,7 @@ type Options struct {
Verbose bool
Quiet bool
Logger *logging.Logger
OCIClient *oci.Client
OCIClient oci.Client
}
// Clean cleans the file path option values

View file

@ -30,7 +30,7 @@ type Options struct {
APIClient api.Client
Logger *logging.Logger
Limit int
OCIClient *oci.Client
OCIClient oci.Client
}
// Clean cleans the file path option values

View file

@ -10,7 +10,7 @@ import (
)
func TestBuildPolicy(t *testing.T) {
ociClient := oci.NewMockClient()
ociClient := oci.MockClient{}
artifactPath := "../test/data/sigstore-js-2.1.0.tgz"
digestAlg := "sha256"

View file

@ -29,7 +29,7 @@ func TestRunVerify(t *testing.T) {
DigestAlgorithm: "sha512",
APIClient: api.NewTestClient(),
Logger: logger,
OCIClient: oci.NewMockClient(),
OCIClient: oci.MockClient{},
OIDCIssuer: GitHubOIDCIssuer,
Owner: "sigstore",
SANRegex: "^https://github.com/sigstore/",
@ -42,7 +42,7 @@ func TestRunVerify(t *testing.T) {
t.Run("with failing OCI artifact fetch", func(t *testing.T) {
opts := publicGoodOpts
opts.ArtifactPath = "oci://ghcr.io/github/test"
opts.OCIClient = oci.NewReferenceFailClient()
opts.OCIClient = oci.ReferenceFailClient{}
err := RunVerify(&opts)
require.Error(t, err)