Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion cli/command/image/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ import (
"context"
"errors"
"fmt"
"io"

"github.com/distribution/reference"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/cli/trust"
"github.com/docker/cli/internal/jsonstream"
"github.com/moby/moby/api/pkg/authconfig"
registrytypes "github.com/moby/moby/api/types/registry"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -84,10 +90,34 @@ func runPull(ctx context.Context, dockerCLI command.Cli, opts pullOptions) error
return err
}
} else {
if err := imagePullPrivileged(ctx, dockerCLI, imgRefAndAuth, opts); err != nil {
if err := imagePullPrivileged(ctx, dockerCLI, imgRefAndAuth.Reference(), imgRefAndAuth.AuthConfig(), opts); err != nil {
return err
}
}
_, _ = fmt.Fprintln(dockerCLI.Out(), imgRefAndAuth.Reference().String())
return nil
}

// imagePullPrivileged pulls the image and displays it to the output
func imagePullPrivileged(ctx context.Context, dockerCLI command.Cli, ref reference.Named, authConfig *registrytypes.AuthConfig, opts pullOptions) error {
encodedAuth, err := authconfig.Encode(*authConfig)
if err != nil {
return err
}
responseBody, err := dockerCLI.Client().ImagePull(ctx, reference.FamiliarString(ref), client.ImagePullOptions{
RegistryAuth: encodedAuth,
PrivilegeFunc: nil,
All: opts.all,
Platform: opts.platform,
})
if err != nil {
return err
}
defer responseBody.Close()

out := dockerCLI.Out()
if opts.quiet {
out = streams.NewOut(io.Discard)
}
return jsonstream.Display(ctx, responseBody, out)
}
42 changes: 7 additions & 35 deletions cli/command/image/trust.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,9 @@ import (
"github.com/distribution/reference"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/cli/trust"
"github.com/docker/cli/internal/jsonstream"
"github.com/docker/cli/internal/registry"
"github.com/moby/moby/api/pkg/authconfig"
registrytypes "github.com/moby/moby/api/types/registry"
"github.com/moby/moby/client"
"github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
notaryclient "github.com/theupdateframework/notary/client"
Expand All @@ -31,16 +27,16 @@ type target struct {

// notaryClientProvider is used in tests to provide a dummy notary client.
type notaryClientProvider interface {
NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error)
NotaryClient() (notaryclient.Repository, error)
}

// newNotaryClient provides a Notary Repository to interact with signed metadata for an image.
func newNotaryClient(cli command.Streams, imgRefAndAuth trust.ImageRefAndAuth) (notaryclient.Repository, error) {
func newNotaryClient(cli command.Streams, repoInfo *trust.RepositoryInfo, authConfig *registrytypes.AuthConfig) (notaryclient.Repository, error) {
if ncp, ok := cli.(notaryClientProvider); ok {
// notaryClientProvider is used in tests to provide a dummy notary client.
return ncp.NotaryClient(imgRefAndAuth, []string{"pull"})
return ncp.NotaryClient()
}
return trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig(), "pull")
return trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, authConfig, "pull")
}

// pushTrustedReference pushes a canonical reference to the trust server.
Expand Down Expand Up @@ -81,7 +77,7 @@ func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.Image
if err != nil {
return err
}
if err := imagePullPrivileged(ctx, cli, updatedImgRefAndAuth, pullOptions{
if err := imagePullPrivileged(ctx, cli, updatedImgRefAndAuth.Reference(), updatedImgRefAndAuth.AuthConfig(), pullOptions{
all: false,
platform: opts.platform,
quiet: opts.quiet,
Expand All @@ -107,7 +103,7 @@ func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.Image
}

func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth) ([]target, error) {
notaryRepo, err := newNotaryClient(cli, imgRefAndAuth)
notaryRepo, err := newNotaryClient(cli, imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig())
if err != nil {
return nil, fmt.Errorf("error establishing connection to trust repository: %w", err)
}
Expand Down Expand Up @@ -155,38 +151,14 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth)
return []target{r}, err
}

// imagePullPrivileged pulls the image and displays it to the output
func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts pullOptions) error {
encodedAuth, err := authconfig.Encode(*imgRefAndAuth.AuthConfig())
if err != nil {
return err
}
responseBody, err := cli.Client().ImagePull(ctx, reference.FamiliarString(imgRefAndAuth.Reference()), client.ImagePullOptions{
RegistryAuth: encodedAuth,
PrivilegeFunc: nil,
All: opts.all,
Platform: opts.platform,
})
if err != nil {
return err
}
defer responseBody.Close()

out := cli.Out()
if opts.quiet {
out = streams.NewOut(io.Discard)
}
return jsonstream.Display(ctx, responseBody, out)
}

// TrustedReference returns the canonical trusted reference for an image reference
func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedTagged) (reference.Canonical, error) {
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, authResolver(cli), ref.String())
if err != nil {
return nil, err
}

notaryRepo, err := newNotaryClient(cli, imgRefAndAuth)
notaryRepo, err := newNotaryClient(cli, imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig())
if err != nil {
return nil, fmt.Errorf("error establishing connection to trust repository: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions cli/command/trust/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ type trustKey struct {

// notaryClientProvider is used in tests to provide a dummy notary client.
type notaryClientProvider interface {
NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (client.Repository, error)
NotaryClient() (client.Repository, error)
}

// newNotaryClient provides a Notary Repository to interact with signed metadata for an image.
func newNotaryClient(cli command.Streams, imgRefAndAuth trust.ImageRefAndAuth, actions []string) (client.Repository, error) {
if ncp, ok := cli.(notaryClientProvider); ok {
// notaryClientProvider is used in tests to provide a dummy notary client.
return ncp.NotaryClient(imgRefAndAuth, actions)
return ncp.NotaryClient()
}
return trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig(), actions...)
}
Expand Down
5 changes: 2 additions & 3 deletions cli/command/trust/inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"io"
"testing"

"github.com/docker/cli/cli/trust"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/notary"
"github.com/theupdateframework/notary/client"
Expand Down Expand Up @@ -48,7 +47,7 @@ func TestTrustInspectCommandRepositoryErrors(t *testing.T) {
testCases := []struct {
doc string
args []string
notaryRepository func(trust.ImageRefAndAuth, []string) (client.Repository, error)
notaryRepository func() (client.Repository, error)
err string
golden string
}{
Expand Down Expand Up @@ -100,7 +99,7 @@ func TestTrustInspectCommand(t *testing.T) {
testCases := []struct {
doc string
args []string
notaryRepository func(trust.ImageRefAndAuth, []string) (client.Repository, error)
notaryRepository func() (client.Repository, error)
golden string
}{
{
Expand Down
3 changes: 1 addition & 2 deletions cli/command/trust/revoke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"io"
"testing"

"github.com/docker/cli/cli/trust"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/notary"
"github.com/theupdateframework/notary/client"
Expand Down Expand Up @@ -60,7 +59,7 @@ func TestTrustRevokeCommand(t *testing.T) {

testCases := []struct {
doc string
notaryRepository func(trust.ImageRefAndAuth, []string) (client.Repository, error)
notaryRepository func() (client.Repository, error)
args []string
expectedErr string
expectedMessage string
Expand Down
8 changes: 4 additions & 4 deletions cli/trust/trust.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func certificateDirectory(server string) (string, error) {
}

// Server returns the base URL for the trust server.
func Server(index *registrytypes.IndexInfo) (string, error) {
func Server(indexName string) (string, error) {
if s := os.Getenv("DOCKER_CONTENT_TRUST_SERVER"); s != "" {
urlObj, err := url.Parse(s)
if err != nil || urlObj.Scheme != "https" {
Expand All @@ -88,10 +88,10 @@ func Server(index *registrytypes.IndexInfo) (string, error) {

return s, nil
}
if index.Official {
if indexName == "docker.io" || indexName == "index.docker.io" {
return NotaryServer, nil
}
return "https://" + index.Name, nil
return "https://" + indexName, nil
}

type simpleCredentialStore struct {
Expand All @@ -117,7 +117,7 @@ const dctDeprecation = `WARNING: Docker is retiring DCT for Docker Official Imag
// information needed to operate on a notary repository.
// It creates an HTTP transport providing authentication support.
func GetNotaryRepository(in io.Reader, out io.Writer, userAgent string, repoInfo *RepositoryInfo, authConfig *registrytypes.AuthConfig, actions ...string) (client.Repository, error) {
server, err := Server(repoInfo.Index)
server, err := Server(repoInfo.Index.Name)
if err != nil {
return nil, err
}
Expand Down
17 changes: 8 additions & 9 deletions cli/trust/trust_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"testing"

"github.com/distribution/reference"
registrytypes "github.com/moby/moby/api/types/registry"
"github.com/opencontainers/go-digest"
"github.com/theupdateframework/notary/client"
"github.com/theupdateframework/notary/trustpinning"
Expand Down Expand Up @@ -56,32 +55,32 @@ func TestGetSignableRolesError(t *testing.T) {

func TestENVTrustServer(t *testing.T) {
t.Setenv("DOCKER_CONTENT_TRUST_SERVER", "https://notary-test.example.com:5000")
indexInfo := &registrytypes.IndexInfo{Name: "testserver"}
output, err := Server(indexInfo)
output, err := Server("testserver")
const expected = "https://notary-test.example.com:5000"
assert.NilError(t, err)
assert.Equal(t, output, expected)
}

func TestHTTPENVTrustServer(t *testing.T) {
t.Setenv("DOCKER_CONTENT_TRUST_SERVER", "http://notary-test.example.com:5000")
indexInfo := &registrytypes.IndexInfo{Name: "testserver"}
_, err := Server(indexInfo)
_, err := Server("testserver")
const expected = "valid https URL required for trust server"
assert.ErrorContains(t, err, expected, "Expected error with invalid scheme")
}

func TestOfficialTrustServer(t *testing.T) {
indexInfo := &registrytypes.IndexInfo{Name: "testserver", Official: true}
output, err := Server(indexInfo)
output, err := Server("docker.io")
const expected = NotaryServer
assert.NilError(t, err)
assert.Equal(t, output, expected)

output, err = Server("index.docker.io")
assert.NilError(t, err)
assert.Equal(t, output, expected)
}

func TestNonOfficialTrustServer(t *testing.T) {
indexInfo := &registrytypes.IndexInfo{Name: "testserver", Official: false}
output, err := Server(indexInfo)
output, err := Server("testserver")
const expected = "https://testserver"
assert.NilError(t, err)
assert.Equal(t, output, expected)
Expand Down
7 changes: 3 additions & 4 deletions internal/test/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ import (
"github.com/docker/cli/cli/context/store"
manifeststore "github.com/docker/cli/cli/manifest/store"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/cli/trust"
"github.com/docker/cli/internal/registryclient"
"github.com/moby/moby/client"
notaryclient "github.com/theupdateframework/notary/client"
)

// NotaryClientFuncType defines a function that returns a fake notary client
type NotaryClientFuncType func(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error)
type NotaryClientFuncType func() (notaryclient.Repository, error)

// FakeCli emulates the default DockerCli
type FakeCli struct {
Expand Down Expand Up @@ -169,9 +168,9 @@ func (c *FakeCli) SetNotaryClient(notaryClientFunc NotaryClientFuncType) {
}

// NotaryClient returns an err for testing unless defined
func (c *FakeCli) NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error) {
func (c *FakeCli) NotaryClient() (notaryclient.Repository, error) {
if c.notaryClientFunc != nil {
return c.notaryClientFunc(imgRefAndAuth, actions)
return c.notaryClientFunc()
}
return nil, errors.New("no notary client available unless defined")
}
Expand Down
11 changes: 5 additions & 6 deletions internal/test/notary/client.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package notary

import (
"github.com/docker/cli/cli/trust"
"github.com/theupdateframework/notary/client"
"github.com/theupdateframework/notary/client/changelist"
"github.com/theupdateframework/notary/cryptoservice"
Expand All @@ -12,7 +11,7 @@ import (
)

// GetOfflineNotaryRepository returns a OfflineNotaryRepository
func GetOfflineNotaryRepository(trust.ImageRefAndAuth, []string) (client.Repository, error) {
func GetOfflineNotaryRepository() (client.Repository, error) {
return OfflineNotaryRepository{}, nil
}

Expand Down Expand Up @@ -146,7 +145,7 @@ func (OfflineNotaryRepository) GetGUN() data.GUN {
}

// GetUninitializedNotaryRepository returns an UninitializedNotaryRepository
func GetUninitializedNotaryRepository(trust.ImageRefAndAuth, []string) (client.Repository, error) {
func GetUninitializedNotaryRepository() (client.Repository, error) {
return UninitializedNotaryRepository{}, nil
}

Expand Down Expand Up @@ -207,7 +206,7 @@ func (UninitializedNotaryRepository) RotateKey(data.RoleName, bool, []string) er
}

// GetEmptyTargetsNotaryRepository returns an EmptyTargetsNotaryRepository
func GetEmptyTargetsNotaryRepository(trust.ImageRefAndAuth, []string) (client.Repository, error) {
func GetEmptyTargetsNotaryRepository() (client.Repository, error) {
return EmptyTargetsNotaryRepository{}, nil
}

Expand Down Expand Up @@ -285,7 +284,7 @@ func (EmptyTargetsNotaryRepository) RotateKey(data.RoleName, bool, []string) err
}

// GetLoadedNotaryRepository returns a LoadedNotaryRepository
func GetLoadedNotaryRepository(trust.ImageRefAndAuth, []string) (client.Repository, error) {
func GetLoadedNotaryRepository() (client.Repository, error) {
return LoadedNotaryRepository{}, nil
}

Expand Down Expand Up @@ -511,7 +510,7 @@ func (l LoadedNotaryRepository) GetCryptoService() signed.CryptoService {
}

// GetLoadedWithNoSignersNotaryRepository returns a LoadedWithNoSignersNotaryRepository
func GetLoadedWithNoSignersNotaryRepository(trust.ImageRefAndAuth, []string) (client.Repository, error) {
func GetLoadedWithNoSignersNotaryRepository() (client.Repository, error) {
return LoadedWithNoSignersNotaryRepository{}, nil
}

Expand Down
Loading