From 80bc7df7820513e79fc24a1d4bbffd96ef6a9adf Mon Sep 17 00:00:00 2001 From: Antonio Gamez Diaz Date: Fri, 8 Jul 2022 21:14:06 +0200 Subject: [PATCH 1/5] feat(helm): add 'ClientOptResolver' ClientOption This is a way to make the containerd resolver configurable by third-party users. Related #10623 Signed-off-by: Antonio Gamez Diaz --- pkg/registry/client.go | 7 +++++++ pkg/registry/client_test.go | 1 + 2 files changed, 8 insertions(+) diff --git a/pkg/registry/client.go b/pkg/registry/client.go index c1004f956..bafd40c5b 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -166,6 +166,13 @@ func ClientOptCredentialsFile(credentialsFile string) ClientOption { } } +// ClientOptResolver returns a function that sets the resolver setting on a client options set +func ClientOptResolver(resolver remotes.Resolver) ClientOption { + return func(client *Client) { + client.resolver = resolver + } +} + type ( // LoginOption allows specifying various settings on login LoginOption func(*loginOperation) diff --git a/pkg/registry/client_test.go b/pkg/registry/client_test.go index 138dd4245..5cc14ffdf 100644 --- a/pkg/registry/client_test.go +++ b/pkg/registry/client_test.go @@ -73,6 +73,7 @@ func (suite *RegistryClientTestSuite) SetupSuite() { ClientOptEnableCache(true), ClientOptWriter(suite.Out), ClientOptCredentialsFile(credentialsFile), + ClientOptResolver(nil), ) suite.Nil(err, "no error creating registry client") From 62be6f1af688233075249ea2787d016d0b77880f Mon Sep 17 00:00:00 2001 From: Antonio Gamez Diaz Date: Fri, 8 Jul 2022 21:15:14 +0200 Subject: [PATCH 2/5] ref(helm): export DescriptorPullSummary fields Exporting those fields enable 3rd party users to build their own mocked PullResult responses. Related #10623 Signed-off-by: Antonio Gamez Diaz --- pkg/registry/client.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/registry/client.go b/pkg/registry/client.go index bafd40c5b..499a46737 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -248,21 +248,21 @@ type ( // PullResult is the result returned upon successful pull. PullResult struct { - Manifest *descriptorPullSummary `json:"manifest"` - Config *descriptorPullSummary `json:"config"` - Chart *descriptorPullSummaryWithMeta `json:"chart"` - Prov *descriptorPullSummary `json:"prov"` + Manifest *DescriptorPullSummary `json:"manifest"` + Config *DescriptorPullSummary `json:"config"` + Chart *DescriptorPullSummaryWithMeta `json:"chart"` + Prov *DescriptorPullSummary `json:"prov"` Ref string `json:"ref"` } - descriptorPullSummary struct { + DescriptorPullSummary struct { Data []byte `json:"-"` Digest string `json:"digest"` Size int64 `json:"size"` } - descriptorPullSummaryWithMeta struct { - descriptorPullSummary + DescriptorPullSummaryWithMeta struct { + DescriptorPullSummary Meta *chart.Metadata `json:"meta"` } @@ -361,16 +361,16 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { } } result := &PullResult{ - Manifest: &descriptorPullSummary{ + Manifest: &DescriptorPullSummary{ Digest: manifest.Digest.String(), Size: manifest.Size, }, - Config: &descriptorPullSummary{ + Config: &DescriptorPullSummary{ Digest: configDescriptor.Digest.String(), Size: configDescriptor.Size, }, - Chart: &descriptorPullSummaryWithMeta{}, - Prov: &descriptorPullSummary{}, + Chart: &DescriptorPullSummaryWithMeta{}, + Prov: &DescriptorPullSummary{}, Ref: parsedRef.String(), } var getManifestErr error From 770c51ef0a4e6c4d1f9d38185971840a56d10ce3 Mon Sep 17 00:00:00 2001 From: Antonio Gamez Diaz Date: Fri, 12 May 2023 11:45:51 +0200 Subject: [PATCH 3/5] Add ClientOptResolver to test util file Signed-off-by: Antonio Gamez Diaz --- pkg/registry/utils_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/registry/utils_test.go b/pkg/registry/utils_test.go index bdeacc712..022d7a282 100644 --- a/pkg/registry/utils_test.go +++ b/pkg/registry/utils_test.go @@ -98,6 +98,7 @@ func setup(suite *TestSuite, tlsEnabled bool, insecure bool) *registry.Registry ClientOptWriter(suite.Out), ClientOptCredentialsFile(credentialsFile), ClientOptHTTPClient(httpClient), + ClientOptResolver(nil), ) } else { suite.RegistryClient, err = NewClient( @@ -105,6 +106,7 @@ func setup(suite *TestSuite, tlsEnabled bool, insecure bool) *registry.Registry ClientOptEnableCache(true), ClientOptWriter(suite.Out), ClientOptCredentialsFile(credentialsFile), + ClientOptResolver(nil), ) } From 197d1defbf8262c22396e061791cf7c4b8559e9b Mon Sep 17 00:00:00 2001 From: Antonio Gamez Diaz Date: Wed, 9 Aug 2023 11:40:57 +0200 Subject: [PATCH 4/5] Add required changes after merge Signed-off-by: Antonio Gamez Diaz --- pkg/registry/client.go | 60 +++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/pkg/registry/client.go b/pkg/registry/client.go index 5c0110e32..95dc6d631 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -87,36 +87,40 @@ func NewClient(options ...ClientOption) (*Client, error) { } client.authorizer = authClient } - client.resolver = func(ref registry.Reference) (remotes.Resolver, error) { - headers := http.Header{} - headers.Set("User-Agent", version.GetUserAgent()) - dockerClient, ok := client.authorizer.(*dockerauth.Client) - if ok { - username, password, err := dockerClient.Credential(ref.Registry) - if err != nil { - return nil, errors.New("unable to retrieve credentials") - } - // A blank returned username and password value is a bearer token - if username == "" && password != "" { - headers.Set("Authorization", fmt.Sprintf("Bearer %s", password)) - } else { - headers.Set("Authorization", fmt.Sprintf("Basic %s", basicAuth(username, password))) + + if client.resolver == nil { + client.resolver = func(ref registry.Reference) (remotes.Resolver, error) { + headers := http.Header{} + headers.Set("User-Agent", version.GetUserAgent()) + dockerClient, ok := client.authorizer.(*dockerauth.Client) + if ok { + username, password, err := dockerClient.Credential(ref.Registry) + if err != nil { + return nil, errors.New("unable to retrieve credentials") + } + // A blank returned username and password value is a bearer token + if username == "" && password != "" { + headers.Set("Authorization", fmt.Sprintf("Bearer %s", password)) + } else { + headers.Set("Authorization", fmt.Sprintf("Basic %s", basicAuth(username, password))) + } } - } - opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)} - if client.httpClient != nil { - opts = append(opts, auth.WithResolverClient(client.httpClient)) - } - if client.plainHTTP { - opts = append(opts, auth.WithResolverPlainHTTP()) - } - resolver, err := client.authorizer.ResolverWithOpts(opts...) - if err != nil { - return nil, err + opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)} + if client.httpClient != nil { + opts = append(opts, auth.WithResolverClient(client.httpClient)) + } + if client.plainHTTP { + opts = append(opts, auth.WithResolverPlainHTTP()) + } + resolver, err := client.authorizer.ResolverWithOpts(opts...) + if err != nil { + return nil, err + } + return resolver, nil } - return resolver, nil } + // allocate a cache if option is set var cache registryauth.Cache if client.enableCache { @@ -202,7 +206,9 @@ func ClientOptPlainHTTP() ClientOption { // ClientOptResolver returns a function that sets the resolver setting on a client options set func ClientOptResolver(resolver remotes.Resolver) ClientOption { return func(client *Client) { - client.resolver = resolver + client.resolver = func(ref registry.Reference) (remotes.Resolver, error) { + return resolver, nil + } } } From 3607cd7110a8e62c69ea02900139c1c54534aaa9 Mon Sep 17 00:00:00 2001 From: Antonio Gamez Diaz Date: Wed, 9 Aug 2023 23:53:56 +0200 Subject: [PATCH 5/5] Avoid nil dereference if passing a nil resolver Signed-off-by: Antonio Gamez Diaz --- pkg/registry/client.go | 58 +++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/pkg/registry/client.go b/pkg/registry/client.go index 95dc6d631..0dfa6926f 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -88,37 +88,43 @@ func NewClient(options ...ClientOption) (*Client, error) { client.authorizer = authClient } - if client.resolver == nil { - client.resolver = func(ref registry.Reference) (remotes.Resolver, error) { - headers := http.Header{} - headers.Set("User-Agent", version.GetUserAgent()) - dockerClient, ok := client.authorizer.(*dockerauth.Client) - if ok { - username, password, err := dockerClient.Credential(ref.Registry) - if err != nil { - return nil, errors.New("unable to retrieve credentials") - } - // A blank returned username and password value is a bearer token - if username == "" && password != "" { - headers.Set("Authorization", fmt.Sprintf("Bearer %s", password)) - } else { - headers.Set("Authorization", fmt.Sprintf("Basic %s", basicAuth(username, password))) - } + resolverFn := client.resolver // copy for avoiding recursive call + client.resolver = func(ref registry.Reference) (remotes.Resolver, error) { + if resolverFn != nil { + // validate if the resolverFn returns a valid resolver + if resolver, err := resolverFn(ref); resolver != nil && err == nil { + return resolver, nil } + } - opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)} - if client.httpClient != nil { - opts = append(opts, auth.WithResolverClient(client.httpClient)) - } - if client.plainHTTP { - opts = append(opts, auth.WithResolverPlainHTTP()) - } - resolver, err := client.authorizer.ResolverWithOpts(opts...) + headers := http.Header{} + headers.Set("User-Agent", version.GetUserAgent()) + dockerClient, ok := client.authorizer.(*dockerauth.Client) + if ok { + username, password, err := dockerClient.Credential(ref.Registry) if err != nil { - return nil, err + return nil, errors.New("unable to retrieve credentials") } - return resolver, nil + // A blank returned username and password value is a bearer token + if username == "" && password != "" { + headers.Set("Authorization", fmt.Sprintf("Bearer %s", password)) + } else { + headers.Set("Authorization", fmt.Sprintf("Basic %s", basicAuth(username, password))) + } + } + + opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)} + if client.httpClient != nil { + opts = append(opts, auth.WithResolverClient(client.httpClient)) + } + if client.plainHTTP { + opts = append(opts, auth.WithResolverPlainHTTP()) + } + resolver, err := client.authorizer.ResolverWithOpts(opts...) + if err != nil { + return nil, err } + return resolver, nil } // allocate a cache if option is set