cli: introduce NotaryClient getter
Signed-off-by: Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
Upstream-commit: e5c35ab9d1
Component: cli
This commit is contained in:
@ -12,13 +12,18 @@ import (
|
||||
cliconfig "github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
dopts "github.com/docker/cli/opts"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/docker/notary"
|
||||
notaryclient "github.com/docker/notary/client"
|
||||
"github.com/docker/notary/passphrase"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
@ -40,6 +45,7 @@ type Cli interface {
|
||||
SetIn(in *InStream)
|
||||
ConfigFile() *configfile.ConfigFile
|
||||
ServerInfo() ServerInfo
|
||||
NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error)
|
||||
}
|
||||
|
||||
// DockerCli is an instance the docker command line client.
|
||||
@ -161,6 +167,51 @@ func getClientWithPassword(passRetriever notary.PassRetriever, newClient func(pa
|
||||
}
|
||||
}
|
||||
|
||||
// NotaryClient provides a Notary Repository to interact with signed metadata for an image
|
||||
func (cli *DockerCli) NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error) {
|
||||
return trust.GetNotaryRepository(cli.In(), cli.Out(), UserAgent(), imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig(), actions...)
|
||||
}
|
||||
|
||||
// GetImageReferencesAndAuth retrieves the necessary reference and auth information for an image name
|
||||
// as a ImageRefAndAuth struct
|
||||
func GetImageReferencesAndAuth(ctx context.Context, cli Cli, imgName string) (*trust.ImageRefAndAuth, error) {
|
||||
ref, err := reference.ParseNormalizedNamed(imgName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Resolve the Repository name from fqn to RepositoryInfo
|
||||
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authConfig := ResolveAuthConfig(ctx, cli, repoInfo.Index)
|
||||
return trust.NewImageRefAndAuth(&authConfig, ref, repoInfo, getTag(ref), getDigest(ref)), nil
|
||||
}
|
||||
|
||||
func getTag(ref reference.Named) string {
|
||||
switch x := ref.(type) {
|
||||
case reference.Canonical, reference.Digested:
|
||||
return ""
|
||||
case reference.NamedTagged:
|
||||
return x.Tag()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func getDigest(ref reference.Named) digest.Digest {
|
||||
switch x := ref.(type) {
|
||||
case reference.Canonical:
|
||||
return x.Digest()
|
||||
case reference.Digested:
|
||||
return x.Digest()
|
||||
default:
|
||||
return digest.Digest("")
|
||||
}
|
||||
}
|
||||
|
||||
// ServerInfo stores details about the supported features and platform of the
|
||||
// server
|
||||
type ServerInfo struct {
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/cli/internal/test/testutil"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
@ -196,3 +197,20 @@ func TestGetClientWithPassword(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTag(t *testing.T) {
|
||||
ref, err := reference.ParseNormalizedNamed("ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2")
|
||||
assert.NoError(t, err)
|
||||
tag := getTag(ref)
|
||||
assert.Equal(t, "", tag)
|
||||
|
||||
ref, err = reference.ParseNormalizedNamed("alpine:latest")
|
||||
assert.NoError(t, err)
|
||||
tag = getTag(ref)
|
||||
assert.Equal(t, tag, "latest")
|
||||
|
||||
ref, err = reference.ParseNormalizedNamed("alpine")
|
||||
assert.NoError(t, err)
|
||||
tag = getTag(ref)
|
||||
assert.Equal(t, tag, "")
|
||||
}
|
||||
|
||||
@ -102,14 +102,13 @@ func PushTrustedReference(streams command.Streams, repoInfo *registry.Repository
|
||||
|
||||
fmt.Fprintln(streams.Out(), "Signing and pushing trust metadata")
|
||||
|
||||
repo, err := trust.GetNotaryRepository(streams, repoInfo, authConfig, "push", "pull")
|
||||
repo, err := trust.GetNotaryRepository(streams.In(), streams.Out(), command.UserAgent(), repoInfo, &authConfig, "push", "pull")
|
||||
if err != nil {
|
||||
fmt.Fprintf(streams.Out(), "Error establishing connection to notary repository: %s\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// get the latest repository metadata so we can figure out which roles to sign
|
||||
// TODO(riyazdf): interface change to get back Update
|
||||
_, err = repo.ListTargets()
|
||||
|
||||
switch err.(type) {
|
||||
@ -185,7 +184,7 @@ func imagePushPrivileged(ctx context.Context, cli command.Cli, authConfig types.
|
||||
func trustedPull(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
|
||||
var refs []target
|
||||
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull")
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, &authConfig, "pull")
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err)
|
||||
return err
|
||||
@ -300,7 +299,7 @@ func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedT
|
||||
// Resolve the Auth config relevant for this server
|
||||
authConfig := command.ResolveAuthConfig(ctx, cli, repoInfo.Index)
|
||||
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull")
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, &authConfig, "pull")
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err)
|
||||
return nil, err
|
||||
|
||||
@ -59,7 +59,7 @@ func trustedResolveDigest(ctx context.Context, cli command.Cli, ref reference.Na
|
||||
|
||||
authConfig := command.ResolveAuthConfig(ctx, cli, repoInfo.Index)
|
||||
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull")
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, &authConfig, "pull")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error establishing connection to trust repository")
|
||||
}
|
||||
|
||||
@ -1,83 +1,15 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
)
|
||||
|
||||
const releasedRoleName = "Repo Admin"
|
||||
|
||||
// ImageRefAndAuth contains all reference information and the auth config for an image request
|
||||
type ImageRefAndAuth struct {
|
||||
authConfig *types.AuthConfig
|
||||
reference reference.Named
|
||||
repoInfo *registry.RepositoryInfo
|
||||
tag string
|
||||
}
|
||||
|
||||
// AuthConfig returns the auth information (username, etc) for a given ImageRefAndAuth
|
||||
func (imgRefAuth *ImageRefAndAuth) AuthConfig() *types.AuthConfig {
|
||||
return imgRefAuth.authConfig
|
||||
}
|
||||
|
||||
// Reference returns the Image reference for a given ImageRefAndAuth
|
||||
func (imgRefAuth *ImageRefAndAuth) Reference() reference.Named {
|
||||
return imgRefAuth.reference
|
||||
}
|
||||
|
||||
// RepoInfo returns the repository information for a given ImageRefAndAuth
|
||||
func (imgRefAuth *ImageRefAndAuth) RepoInfo() *registry.RepositoryInfo {
|
||||
return imgRefAuth.repoInfo
|
||||
}
|
||||
|
||||
// Tag returns the Image tag for a given ImageRefAndAuth
|
||||
func (imgRefAuth *ImageRefAndAuth) Tag() string {
|
||||
return imgRefAuth.tag
|
||||
}
|
||||
|
||||
func getImageReferencesAndAuth(ctx context.Context, cli command.Cli, imgName string) (*ImageRefAndAuth, error) {
|
||||
ref, err := reference.ParseNormalizedNamed(imgName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag, err := getTag(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Resolve the Repository name from fqn to RepositoryInfo
|
||||
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authConfig := command.ResolveAuthConfig(ctx, cli, repoInfo.Index)
|
||||
return &ImageRefAndAuth{&authConfig, ref, repoInfo, tag}, err
|
||||
}
|
||||
|
||||
func getTag(ref reference.Named) (string, error) {
|
||||
var tag string
|
||||
switch x := ref.(type) {
|
||||
case reference.Canonical:
|
||||
return "", fmt.Errorf("cannot use a digest reference for IMAGE:TAG")
|
||||
case reference.NamedTagged:
|
||||
tag = x.Tag()
|
||||
default:
|
||||
tag = ""
|
||||
}
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// check if a role name is "released": either targets/releases or targets TUF roles
|
||||
func isReleasedTarget(role data.RoleName) bool {
|
||||
return role == data.CanonicalTargetsRole || role == trust.ReleasesRole
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetTag(t *testing.T) {
|
||||
ref, err := reference.ParseNormalizedNamed("ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2")
|
||||
assert.NoError(t, err)
|
||||
tag, err := getTag(ref)
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "cannot use a digest reference for IMAGE:TAG")
|
||||
|
||||
ref, err = reference.ParseNormalizedNamed("alpine:latest")
|
||||
assert.NoError(t, err)
|
||||
tag, err = getTag(ref)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tag, "latest")
|
||||
|
||||
ref, err = reference.ParseNormalizedNamed("alpine")
|
||||
assert.NoError(t, err)
|
||||
tag, err = getTag(ref)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tag, "")
|
||||
}
|
||||
@ -59,12 +59,12 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
func lookupTrustInfo(cli command.Cli, remote string) error {
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := getImageReferencesAndAuth(ctx, cli, remote)
|
||||
imgRefAndAuth, err := command.GetImageReferencesAndAuth(ctx, cli, remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tag := imgRefAndAuth.Tag()
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli, imgRefAndAuth.RepoInfo(), *imgRefAndAuth.AuthConfig(), "pull")
|
||||
notaryRepo, err := cli.NotaryClient(*imgRefAndAuth, trust.ActionsPullOnly)
|
||||
if err != nil {
|
||||
return trust.NotaryError(imgRefAndAuth.Reference().Name(), err)
|
||||
}
|
||||
|
||||
@ -36,11 +36,14 @@ func newRevokeCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
func revokeTrust(cli command.Cli, remote string, options revokeOptions) error {
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := getImageReferencesAndAuth(ctx, cli, remote)
|
||||
imgRefAndAuth, err := command.GetImageReferencesAndAuth(ctx, cli, remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tag := imgRefAndAuth.Tag()
|
||||
if imgRefAndAuth.Tag() == "" && imgRefAndAuth.Digest() != "" {
|
||||
return fmt.Errorf("cannot use a digest reference for IMAGE:TAG")
|
||||
}
|
||||
if imgRefAndAuth.Tag() == "" && !options.forceYes {
|
||||
deleteRemote := command.PromptForConfirmation(os.Stdin, cli.Out(), fmt.Sprintf("Please confirm you would like to delete all signature data for %s?", remote))
|
||||
if !deleteRemote {
|
||||
@ -49,7 +52,7 @@ func revokeTrust(cli command.Cli, remote string, options revokeOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli, imgRefAndAuth.RepoInfo(), *imgRefAndAuth.AuthConfig(), "push", "pull")
|
||||
notaryRepo, err := cli.NotaryClient(*imgRefAndAuth, trust.ActionsPushAndPull)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -31,16 +31,19 @@ func newSignCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
func signImage(cli command.Cli, imageName string) error {
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := getImageReferencesAndAuth(ctx, cli, imageName)
|
||||
imgRefAndAuth, err := command.GetImageReferencesAndAuth(ctx, cli, imageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tag := imgRefAndAuth.Tag()
|
||||
if tag == "" {
|
||||
if imgRefAndAuth.Digest() != "" {
|
||||
return fmt.Errorf("cannot use a digest reference for IMAGE:TAG")
|
||||
}
|
||||
return fmt.Errorf("No tag specified for %s", imageName)
|
||||
}
|
||||
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli, imgRefAndAuth.RepoInfo(), *imgRefAndAuth.AuthConfig(), "push", "pull")
|
||||
notaryRepo, err := cli.NotaryClient(*imgRefAndAuth, trust.ActionsPushAndPull)
|
||||
if err != nil {
|
||||
return trust.NotaryError(imgRefAndAuth.Reference().Name(), err)
|
||||
}
|
||||
@ -50,7 +53,6 @@ func signImage(cli command.Cli, imageName string) error {
|
||||
defer clearChangeList(notaryRepo)
|
||||
|
||||
// get the latest repository metadata so we can figure out which roles to sign
|
||||
// TODO(riyazdf): interface change to get back Update
|
||||
if _, err = notaryRepo.ListTargets(); err != nil {
|
||||
switch err.(type) {
|
||||
case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist:
|
||||
|
||||
Reference in New Issue
Block a user