Skip to content

Commit a501a80

Browse files
committed
WIP: don't require IndexInfo
Lots to do here; too many wrappers everywhere, which may become easier when content trust is removed (which adds another level of abstraction) Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent f03fb6c commit a501a80

9 files changed

Lines changed: 68 additions & 36 deletions

File tree

cli/command/image/push.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,14 @@ To push the complete multi-platform image, remove the --platform flag.
106106
}
107107
}
108108

109-
// Resolve the Repository name from fqn to RepositoryInfo
110-
repoInfo, _ := registry.ParseRepositoryInfo(ref)
111-
112109
// Resolve the Auth config relevant for this server
113-
authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), repoInfo.Index)
114-
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
110+
encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), ref.String())
115111
if err != nil {
116112
return err
117113
}
118114
var requestPrivilege registrytypes.RequestAuthConfig
119115
if dockerCli.In().IsTerminal() {
120-
requestPrivilege = command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "push")
116+
requestPrivilege = command.NewAuthRequester(dockerCli, reference.Domain(ref), "Login prior to push:")
121117
}
122118
options := image.PushOptions{
123119
All: opts.all,
@@ -139,6 +135,9 @@ To push the complete multi-platform image, remove the --platform flag.
139135

140136
defer responseBody.Close()
141137
if !opts.untrusted {
138+
repoInfo, _ := registry.ParseRepositoryInfo(ref)
139+
authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), repoInfo.Index)
140+
142141
// TODO pushTrustedReference currently doesn't respect `--quiet`
143142
return pushTrustedReference(ctx, dockerCli, repoInfo, ref, authConfig, responseBody)
144143
}

cli/command/image/trust.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,16 +145,17 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth)
145145

146146
// imagePullPrivileged pulls the image and displays it to the output
147147
func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts pullOptions) error {
148+
// TODO(thaJeztah): get rid of this trust.ImageRefAndAuth monstrosity; we're wrapping wrappers around wrappers; all we need here is the image ref (or even less: the registry name)
148149
encodedAuth, err := registrytypes.EncodeAuthConfig(*imgRefAndAuth.AuthConfig())
149150
if err != nil {
150151
return err
151152
}
152153
var requestPrivilege registrytypes.RequestAuthConfig
153154
if cli.In().IsTerminal() {
154-
requestPrivilege = command.RegistryAuthenticationPrivilegedFunc(cli, imgRefAndAuth.RepoInfo().Index, "pull")
155+
requestPrivilege = command.NewAuthRequester(cli, reference.Domain(imgRefAndAuth.Reference()), "Login prior to pull:")
155156
}
156157
responseBody, err := cli.Client().ImagePull(ctx, reference.FamiliarString(imgRefAndAuth.Reference()), image.PullOptions{
157-
RegistryAuth: encodedAuth,
158+
RegistryAuth: encodedAuth, // TODO: can we always use PrivilegeFunc; lazily fetch creds (maybe we need a way to tell it to either use "stored", or request login)
158159
PrivilegeFunc: requestPrivilege,
159160
All: opts.all,
160161
Platform: opts.platform,

cli/command/plugin/install.go

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"github.com/docker/cli/internal/prompt"
1414
"github.com/docker/docker/api/types"
1515
registrytypes "github.com/docker/docker/api/types/registry"
16-
"github.com/docker/docker/registry"
1716
"github.com/pkg/errors"
1817
"github.com/spf13/cobra"
1918
"github.com/spf13/pflag"
@@ -65,7 +64,15 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti
6564
return types.PluginInstallOptions{}, err
6665
}
6766

68-
repoInfo, _ := registry.ParseRepositoryInfo(ref)
67+
// TODO(thaJeztah): looks like we need to do this here, because "ref" is mutated further below? (because .. docker content trust?)
68+
var requestPrivilege registrytypes.RequestAuthConfig
69+
if dockerCli.In().IsTerminal() {
70+
requestPrivilege = command.NewAuthRequester(dockerCli, reference.Domain(ref), fmt.Sprintf("Login prior to %s:", cmdName))
71+
}
72+
encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), ref.String())
73+
if err != nil {
74+
return types.PluginInstallOptions{}, err
75+
}
6976

7077
remote := ref.String()
7178

@@ -84,17 +91,6 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti
8491
remote = reference.FamiliarString(trusted)
8592
}
8693

87-
authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), repoInfo.Index)
88-
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
89-
if err != nil {
90-
return types.PluginInstallOptions{}, err
91-
}
92-
93-
var requestPrivilege registrytypes.RequestAuthConfig
94-
if dockerCli.In().IsTerminal() {
95-
requestPrivilege = command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, cmdName)
96-
}
97-
9894
options := types.PluginInstallOptions{
9995
RegistryAuth: encodedAuth,
10096
RemoteRef: remote,

cli/command/plugin/push.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"github.com/docker/cli/cli/command"
99
"github.com/docker/cli/cli/trust"
1010
"github.com/docker/cli/internal/jsonstream"
11-
registrytypes "github.com/docker/docker/api/types/registry"
1211
"github.com/docker/docker/registry"
1312
"github.com/pkg/errors"
1413
"github.com/spf13/cobra"
@@ -49,9 +48,7 @@ func runPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
4948

5049
named = reference.TagNameOnly(named)
5150

52-
repoInfo, _ := registry.ParseRepositoryInfo(named)
53-
authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), repoInfo.Index)
54-
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
51+
encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), named.String())
5552
if err != nil {
5653
return err
5754
}
@@ -63,6 +60,8 @@ func runPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
6360
defer responseBody.Close()
6461

6562
if !opts.untrusted {
63+
repoInfo, _ := registry.ParseRepositoryInfo(named)
64+
authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), repoInfo.Index)
6665
return trust.PushTrustedReference(ctx, dockerCli, repoInfo, named, authConfig, responseBody, command.UserAgent())
6766
}
6867

cli/command/registry.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,40 @@ const (
3434
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker/registry#IndexServer
3535
const authConfigKey = "https://index.docker.io/v1/"
3636

37+
// NewAuthRequester returns a RequestPrivilegeFunc for the specified registry
38+
// and the given cmdName (used as informational message to the user).
39+
//
40+
// The returned function is a [registrytypes.RequestAuthConfig] to prompt the user
41+
// for credentials if needed. It is called as fallback if the credentials (if any)
42+
// used for the initial operation did not work.
43+
//
44+
// TODO(thaJeztah): cli Cli could be a Streams if it was not for cli.SetIn to be needed?
45+
// TODO(thaJeztah): ideally, this would accept reposName / imageRef as a regular string (we can parse it if needed!), or .. maybe generics and accept either?
46+
func NewAuthRequester(cli Cli, indexServer string, promptMsg string) registrytypes.RequestAuthConfig {
47+
configKey := getAuthConfigKey(indexServer)
48+
return newPrivilegeFunc(cli, configKey, promptMsg)
49+
}
50+
3751
// RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info
3852
// for the given command to prompt the user for username and password.
53+
//
54+
// Deprecated: this function was only used internally and will be removed in the next release. Use
3955
func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInfo, cmdName string) registrytypes.RequestAuthConfig {
40-
configKey := getAuthConfigKey(index.Name)
41-
isDefaultRegistry := configKey == authConfigKey || index.Official
56+
indexServer := index.Name
57+
configKey := getAuthConfigKey(indexServer)
58+
if index.Official {
59+
configKey = authConfigKey
60+
}
61+
promptMsg := fmt.Sprintf("Login prior to %s:", cmdName)
62+
return newPrivilegeFunc(cli, configKey, promptMsg)
63+
}
64+
65+
func newPrivilegeFunc(cli Cli, indexServer string, promptMsg string) registrytypes.RequestAuthConfig {
4266
return func(ctx context.Context) (string, error) {
43-
_, _ = fmt.Fprintf(cli.Out(), "\nLogin prior to %s:\n", cmdName)
44-
authConfig, err := GetDefaultAuthConfig(cli.ConfigFile(), true, configKey, isDefaultRegistry)
67+
// TODO(thaJeztah): can we make the prompt an argument? ("prompt func()" or "prompt func()?
68+
_, _ = fmt.Fprint(cli.Out(), "\n"+promptMsg+"\n")
69+
isDefaultRegistry := indexServer == authConfigKey
70+
authConfig, err := GetDefaultAuthConfig(cli.ConfigFile(), true, indexServer, isDefaultRegistry)
4571
if err != nil {
4672
_, _ = fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", configKey, err)
4773
}
@@ -67,7 +93,8 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
6793
// It is similar to [registry.ResolveAuthConfig], but uses the credentials-
6894
// store, instead of looking up credentials from a map.
6995
func ResolveAuthConfig(cfg *configfile.ConfigFile, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
70-
configKey := index.Name
96+
indexServer := index.Name
97+
configKey := getAuthConfigKey(indexServer)
7198
if index.Official {
7299
configKey = authConfigKey
73100
}
@@ -80,6 +107,7 @@ func ResolveAuthConfig(cfg *configfile.ConfigFile, index *registrytypes.IndexInf
80107
// If credentials for given serverAddress exists in the credential store, the configuration will be populated with values in it
81108
func GetDefaultAuthConfig(cfg *configfile.ConfigFile, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (registrytypes.AuthConfig, error) {
82109
if !isDefaultRegistry {
110+
// FIXME(thaJeztah): should the same logic be used for getAuthConfigKey ?? Looks like we're normalizing things here, but not elsewhere? why?
83111
serverAddress = credentials.ConvertToHostname(serverAddress)
84112
}
85113
authconfig := configtypes.AuthConfig{}
@@ -123,6 +151,8 @@ func ConfigureAuth(ctx context.Context, cli Cli, flUser, flPassword string, auth
123151
// If defaultUsername is not empty, the username prompt includes that username
124152
// and the user can hit enter without inputting a username to use that default
125153
// username.
154+
//
155+
// TODO(thaJeztah): cli Cli could be a Streams if it was not for cli.SetIn to be needed?
126156
func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword, defaultUsername, serverAddress string) (registrytypes.AuthConfig, error) {
127157
// On Windows, force the use of the regular OS stdin stream.
128158
//
@@ -215,12 +245,15 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
215245
//
216246
// For details on base64url encoding, see:
217247
// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5
248+
//
249+
// FIXME(thaJeztah): do we need a variant like this, but with "indexServer" (domainName) as input?
218250
func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (string, error) {
219251
// Retrieve encoded auth token from the image reference
220252
authConfig, err := resolveAuthConfigFromImage(cfg, image)
221253
if err != nil {
222254
return "", err
223255
}
256+
// FIXME(thaJeztah); implement stringer or "MarshalText / UnmarshalText" on AuthConfig, so that we can pass it around as-is, and encode where it needs to be encoded (when making requests).
224257
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
225258
if err != nil {
226259
return "", err
@@ -229,6 +262,8 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin
229262
}
230263

231264
// resolveAuthConfigFromImage retrieves that AuthConfig using the image string
265+
//
266+
// TODO(thaJeztah): export this and/or accept an image ref-type, and use instead of ResolveAuthConfig
232267
func resolveAuthConfigFromImage(cfg *configfile.ConfigFile, image string) (registrytypes.AuthConfig, error) {
233268
registryRef, err := reference.ParseNormalizedNamed(image)
234269
if err != nil {

cli/command/registry/search.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ func runSearch(ctx context.Context, dockerCli command.Cli, options searchOptions
5252
if options.filter.Value().Contains("is-automated") {
5353
_, _ = fmt.Fprintln(dockerCli.Err(), `WARNING: the "is-automated" filter is deprecated, and searching for "is-automated=true" will not yield any results in future.`)
5454
}
55+
// FIXME(thaJeztah): we need some equivalent of "splitReposSearchTerm" to get the registry hostname from the search term.
5556
indexInfo, err := registry.ParseSearchIndexInfo(options.term)
5657
if err != nil {
5758
return err
@@ -65,7 +66,8 @@ func runSearch(ctx context.Context, dockerCli command.Cli, options searchOptions
6566

6667
var requestPrivilege registrytypes.RequestAuthConfig
6768
if dockerCli.In().IsTerminal() {
68-
requestPrivilege = command.RegistryAuthenticationPrivilegedFunc(dockerCli, indexInfo, "search")
69+
indexServer := indexInfo.Name
70+
requestPrivilege = command.NewAuthRequester(dockerCli, indexServer, "Login prior to search:")
6971
}
7072
results, err := dockerCli.Client().ImageSearch(ctx, options.term, registrytypes.SearchOptions{
7173
RegistryAuth: encodedAuth,

cli/command/trust/sign.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func runSignImage(ctx context.Context, dockerCLI command.Cli, options signOption
8484
}
8585
var requestPrivilege registrytypes.RequestAuthConfig
8686
if dockerCLI.In().IsTerminal() {
87-
requestPrivilege = command.RegistryAuthenticationPrivilegedFunc(dockerCLI, imgRefAndAuth.RepoInfo().Index, "push")
87+
requestPrivilege = command.NewAuthRequester(dockerCLI, reference.Domain(imgRefAndAuth.Reference()), "Login prior to push:")
8888
}
8989
target, err := createTarget(notaryRepo, imgRefAndAuth.Tag())
9090
if err != nil || options.local {

cli/registry/client/endpoint.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ import (
1414
)
1515

1616
type repositoryEndpoint struct {
17-
repoName reference.Named
17+
repoName string
1818
indexInfo *registrytypes.IndexInfo
1919
endpoint registry.APIEndpoint
2020
actions []string
2121
}
2222

2323
// Name returns the repository name
2424
func (r repositoryEndpoint) Name() string {
25-
return reference.Path(r.repoName)
25+
return r.repoName
2626
}
2727

2828
// BaseURL returns the endpoint url
@@ -43,7 +43,7 @@ func newDefaultRepositoryEndpoint(ref reference.Named, insecure bool) (repositor
4343
endpoint.TLSConfig.InsecureSkipVerify = true
4444
}
4545
return repositoryEndpoint{
46-
repoName: repoName,
46+
repoName: reference.Path(repoName),
4747
indexInfo: indexInfo,
4848
endpoint: endpoint,
4949
}, nil

cli/registry/client/fetcher.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ func (c *client) iterateEndpoints(ctx context.Context, namedRef reference.Named,
237237
endpoint.TLSConfig.InsecureSkipVerify = true
238238
}
239239
repoEndpoint := repositoryEndpoint{
240-
repoName: repoName,
240+
repoName: reference.Path(repoName),
241241
indexInfo: indexInfo,
242242
endpoint: endpoint,
243243
}

0 commit comments

Comments
 (0)