cli/pkg/cmd/attestation/artifact/oci/client.go
Meredith Lancaster 02a1274eaa cleanup error wrapping
Signed-off-by: Meredith Lancaster <malancas@github.com>
2024-03-18 11:57:44 -06:00

70 lines
2.3 KiB
Go

package oci
import (
"errors"
"fmt"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
)
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 interface {
GetImageDigest(imgName string) (*v1.Hash, error)
}
func checkForUnauthorizedOrDeniedErr(err transport.Error) error {
for _, diagnostic := range err.Errors {
switch diagnostic.Code {
case transport.UnauthorizedErrorCode:
return ErrRegistryAuthz
case transport.DeniedErrorCode:
return ErrDenied
}
}
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 LiveClient) GetImageDigest(imgName string) (*v1.Hash, error) {
name, err := c.parseReference(imgName)
if err != nil {
return nil, fmt.Errorf("failed to create image tag: %v", err)
}
// The user must already be authenticated with the container registry
// The authn.DefaultKeychain argument indicates that Get should checks the
// user's configuration for the registry credentials
desc, err := c.get(name, remote.WithAuthFromKeychain(authn.DefaultKeychain))
if err != nil {
var transportErr *transport.Error
if errors.As(err, &transportErr) {
if accessErr := checkForUnauthorizedOrDeniedErr(*transportErr); accessErr != nil {
return nil, accessErr
}
}
return nil, fmt.Errorf("failed to fetch remote image: %v", err)
}
return &desc.Digest, nil
}
// Unlike other parts of this command set, we cannot pass a custom HTTP client
// to the go-containerregistry library. This means we have limited visibility
// into the HTTP requests being made to container registries.
func NewLiveClient() *LiveClient {
return &LiveClient{
parseReference: name.ParseReference,
get: remote.Get,
}
}