create interface for oci client
Signed-off-by: Meredith Lancaster <malancas@github.com>
This commit is contained in:
parent
be6b042039
commit
f055517baa
12 changed files with 110 additions and 71 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
35
pkg/cmd/attestation/artifact/oci/mock_client.go
Normal file
35
pkg/cmd/attestation/artifact/oci/mock_client.go
Normal 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
|
||||
}
|
||||
|
|
@ -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}}}
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -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(), "")
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue