From 301e3a12d55c279ef3bd0b1976bafd43bc1e0b7b Mon Sep 17 00:00:00 2001 From: kimsungmin1 Date: Thu, 28 Aug 2025 14:43:49 +0900 Subject: [PATCH 01/16] fix scope when helm push to a registry that use token auth Signed-off-by: kimsungmin1 --- go.mod | 2 + go.sum | 4 + pkg/registry/client.go | 9 ++ pkg/registry/client_http_test.go | 2 +- pkg/registry/client_insecure_tls_test.go | 2 +- pkg/registry/client_scope_test.go | 109 +++++++++++++++++++++++ pkg/registry/client_tls_test.go | 2 +- pkg/registry/registry_test.go | 33 +++++-- 8 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 pkg/registry/client_scope_test.go diff --git a/go.mod b/go.mod index d85d0387a..cb512b54b 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( github.com/moby/term v0.5.2 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 + github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/rubenv/sql-migrate v1.8.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/spf13/cobra v1.10.2 @@ -80,6 +81,7 @@ require ( github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect diff --git a/go.sum b/go.sum index a92d35dfe..0c237ab46 100644 --- a/go.sum +++ b/go.sum @@ -105,6 +105,8 @@ github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8b github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -241,6 +243,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= +github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= diff --git a/pkg/registry/client.go b/pkg/registry/client.go index 77f3d3efd..366251c60 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -717,6 +717,8 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu repository.PlainHTTP = c.plainHTTP repository.Client = c.authorizer + ctx = WithScopeHint(ctx, repository, auth.ActionPush, auth.ActionPull) + manifestDescriptor, err = oras.ExtendedCopy(ctx, memoryStore, parsedRef.String(), repository, parsedRef.String(), oras.DefaultExtendedCopyOptions) if err != nil { return nil, err @@ -927,3 +929,10 @@ func (c *Client) tagManifest(ctx context.Context, memoryStore *memory.Store, return oras.TagBytes(ctx, memoryStore, ocispec.MediaTypeImageManifest, manifestData, parsedRef.String()) } + +func WithScopeHint(ctx context.Context, target any, actions ...string) context.Context { + if repo, ok := target.(*remote.Repository); ok { + return auth.AppendRepositoryScope(ctx, repo.Reference, actions...) + } + return ctx +} diff --git a/pkg/registry/client_http_test.go b/pkg/registry/client_http_test.go index 3eb74c541..316a82d3f 100644 --- a/pkg/registry/client_http_test.go +++ b/pkg/registry/client_http_test.go @@ -30,7 +30,7 @@ type HTTPRegistryClientTestSuite struct { func (suite *HTTPRegistryClientTestSuite) SetupSuite() { // init test client - setup(&suite.TestRegistry, false, false) + setup(&suite.TestRegistry, false, false, "htpasswd") } func (suite *HTTPRegistryClientTestSuite) TearDownSuite() { diff --git a/pkg/registry/client_insecure_tls_test.go b/pkg/registry/client_insecure_tls_test.go index 8c4e928e4..ae0a44337 100644 --- a/pkg/registry/client_insecure_tls_test.go +++ b/pkg/registry/client_insecure_tls_test.go @@ -29,7 +29,7 @@ type InsecureTLSRegistryClientTestSuite struct { func (suite *InsecureTLSRegistryClientTestSuite) SetupSuite() { // init test client - setup(&suite.TestRegistry, true, true) + setup(&suite.TestRegistry, true, true, "htpasswd") } func (suite *InsecureTLSRegistryClientTestSuite) TearDownSuite() { diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go new file mode 100644 index 000000000..245af5e3e --- /dev/null +++ b/pkg/registry/client_scope_test.go @@ -0,0 +1,109 @@ +/* +Copyright The Helm Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package registry + +import ( + "context" + "fmt" + "log" + "net/http" + "os" + "testing" + + "github.com/stretchr/testify/suite" +) + +type RegistryScopeTestSuite struct { + TestSuite +} + +func (suite *RegistryScopeTestSuite) SetupSuite() { + // set registry use token auth + dockerRegistry := setup(&suite.TestSuite, true, true, "token") + // Start Docker registry + go dockerRegistry.ListenAndServe() +} +func (suite *RegistryScopeTestSuite) TearDownSuite() { + teardown(&suite.TestSuite) + os.RemoveAll(suite.WorkspaceDir) +} + +func (suite *RegistryScopeTestSuite) Test_1_Cehck_Push_Request_Scope() { + + //set simple auth server to check the auth request scope + server := &http.Server{ + Addr: suite.AuthServerHost, + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.Equal(string("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice"), r.URL.String()) + w.WriteHeader(http.StatusOK) + }), + } + go func() { + err := server.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatalf("http server failed to ListenAndServe:%v", err) + } + }() + + // basic push, good ref + testingChartCreationTime := "1977-09-02T22:04:05Z" + chartData, err := os.ReadFile("../downloader/testdata/local-subchart-0.1.0.tgz") + suite.Nil(err, "no error loading test chart") + meta, err := extractChartMeta(chartData) + suite.Nil(err, "no error extracting chart meta") + ref := fmt.Sprintf("%s/testrepo/%s:%s", suite.DockerRegistryHost, meta.Name, meta.Version) + _, err = suite.RegistryClient.Push(chartData, ref, PushOptCreationTime(testingChartCreationTime)) + suite.NotNil(err, "error pushing good ref because auth server don't give proper token") + + //shutdown auth server + err = server.Shutdown(context.Background()) + suite.Nil(err, "shutdown simple auth server") + +} + +func (suite *RegistryScopeTestSuite) Test_2_Cehck_Pull_Request_Scope() { + + //set simple auth server to check the auth request scope + server := &http.Server{ + Addr: suite.AuthServerHost, + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.Equal(string("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull&service=testservice"), r.URL.String()) + w.WriteHeader(http.StatusOK) + }), + } + go func() { + err := server.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatalf("http server failed to ListenAndServe:%v", err) + } + }() + + // Load test chart (to build ref pushed in previous test) + // Simple pull, chart only + chartData, err := os.ReadFile("../downloader/testdata/local-subchart-0.1.0.tgz") + suite.Nil(err, "no error loading test chart") + meta, err := extractChartMeta(chartData) + suite.Nil(err, "no error extracting chart meta") + ref := fmt.Sprintf("%s/testrepo/%s:%s", suite.DockerRegistryHost, meta.Name, meta.Version) + _, err = suite.RegistryClient.Pull(ref) + suite.NotNil(err, "error pulling a simple chart because auth server don't give proper token") + + //shutdown auth server + err = server.Shutdown(context.Background()) + suite.Nil(err, "shutdown simple auth server") +} + +func TestRegistryScopeTestSuite(t *testing.T) { + suite.Run(t, new(RegistryScopeTestSuite)) +} diff --git a/pkg/registry/client_tls_test.go b/pkg/registry/client_tls_test.go index 2bea0377f..45eff232c 100644 --- a/pkg/registry/client_tls_test.go +++ b/pkg/registry/client_tls_test.go @@ -31,7 +31,7 @@ type TLSRegistryClientTestSuite struct { func (suite *TLSRegistryClientTestSuite) SetupSuite() { // init test client - setup(&suite.TestRegistry, true, false) + setup(&suite.TestRegistry, true, false, "htpasswd") } func (suite *TLSRegistryClientTestSuite) TearDownSuite() { diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index dc8118c6e..76d6fcb16 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -33,8 +33,10 @@ import ( "github.com/distribution/distribution/v3/configuration" "github.com/distribution/distribution/v3/registry" _ "github.com/distribution/distribution/v3/registry/auth/htpasswd" + _ "github.com/distribution/distribution/v3/registry/auth/token" _ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/phayes/freeport" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "golang.org/x/crypto/bcrypt" @@ -55,6 +57,8 @@ var ( testHtpasswdFileBasename = "authtest.htpasswd" testUsername = "myuser" testPassword = "mypass" + testIssuer = "testissuer" + testService = "testservice" ) type TestRegistry struct { @@ -62,13 +66,14 @@ type TestRegistry struct { Out io.Writer FakeRegistryHost string DockerRegistryHost string + AuthServerHost string CompromisedRegistryHost string WorkspaceDir string RegistryClient *Client dockerRegistry *registry.Registry } -func setup(suite *TestRegistry, tlsEnabled, insecure bool) { +func setup(suite *TestRegistry, tlsEnabled, insecure bool, auth string) { suite.WorkspaceDir = testWorkspaceDir err := os.RemoveAll(suite.WorkspaceDir) require.NoError(suite.T(), err, "no error removing test workspace dir") @@ -139,11 +144,27 @@ func setup(suite *TestRegistry, tlsEnabled, insecure bool) { config.HTTP.DrainTimeout = time.Duration(10) * time.Second config.Storage = map[string]configuration.Parameters{"inmemory": map[string]any{}} - config.Auth = configuration.Auth{ - "htpasswd": configuration.Parameters{ - "realm": "localhost", - "path": htpasswdPath, - }, + if auth == "token" { + port, err := freeport.GetFreePort() + suite.Nil(err, "no error finding free port for test auth server") + + suite.AuthServerHost = fmt.Sprintf("localhost:%d", port) + + config.Auth = configuration.Auth{ + "token": configuration.Parameters{ + "realm": "http://" + suite.AuthServerHost + "/auth", + "service": testService, + "issuer": testIssuer, + "rootcertbundle": tlsServerCert, + }, + } + } else { + config.Auth = configuration.Auth{ + "htpasswd": configuration.Parameters{ + "realm": "localhost", + "path": htpasswdPath, + }, + } } // config tls From 319e06cda30e706db541066d5fe07249b0ed656c Mon Sep 17 00:00:00 2001 From: kimsungmin1 Date: Thu, 28 Aug 2025 14:53:14 +0900 Subject: [PATCH 02/16] add newline in license header Signed-off-by: kimsungmin1 --- pkg/registry/client_scope_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go index 245af5e3e..707e9eed6 100644 --- a/pkg/registry/client_scope_test.go +++ b/pkg/registry/client_scope_test.go @@ -1,9 +1,12 @@ /* Copyright The Helm Authors. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. From fc743b2a1be47c69cc3bf58e0fdc023f8844f866 Mon Sep 17 00:00:00 2001 From: kimsungmin1 Date: Fri, 29 Aug 2025 09:34:11 +0900 Subject: [PATCH 03/16] remove freeport dependency Signed-off-by: kimsungmin1 --- go.mod | 1 - go.sum | 2 -- pkg/registry/registry_test.go | 7 ++++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index cb512b54b..ca3d23bb2 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,6 @@ require ( github.com/moby/term v0.5.2 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 - github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/rubenv/sql-migrate v1.8.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/spf13/cobra v1.10.2 diff --git a/go.sum b/go.sum index 0c237ab46..2d8184ab0 100644 --- a/go.sum +++ b/go.sum @@ -243,8 +243,6 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index 76d6fcb16..5f745c8a1 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -36,7 +36,6 @@ import ( _ "github.com/distribution/distribution/v3/registry/auth/token" _ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/phayes/freeport" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "golang.org/x/crypto/bcrypt" @@ -145,10 +144,12 @@ func setup(suite *TestRegistry, tlsEnabled, insecure bool, auth string) { config.Storage = map[string]configuration.Parameters{"inmemory": map[string]any{}} if auth == "token" { - port, err := freeport.GetFreePort() + ln, err := net.Listen("tcp", "127.0.0.1:0") suite.Nil(err, "no error finding free port for test auth server") + defer ln.Close() - suite.AuthServerHost = fmt.Sprintf("localhost:%d", port) + //set test auth server host + suite.AuthServerHost = ln.Addr().String() config.Auth = configuration.Auth{ "token": configuration.Parameters{ From 9d77ccd46ddf9b2675137891c159d1de35674072 Mon Sep 17 00:00:00 2001 From: kimsungmin1 Date: Tue, 9 Sep 2025 10:00:54 +0900 Subject: [PATCH 04/16] fix typo Signed-off-by: kimsungmin1 --- pkg/registry/client_scope_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go index 707e9eed6..37c77d7d3 100644 --- a/pkg/registry/client_scope_test.go +++ b/pkg/registry/client_scope_test.go @@ -42,7 +42,7 @@ func (suite *RegistryScopeTestSuite) TearDownSuite() { os.RemoveAll(suite.WorkspaceDir) } -func (suite *RegistryScopeTestSuite) Test_1_Cehck_Push_Request_Scope() { +func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { //set simple auth server to check the auth request scope server := &http.Server{ @@ -75,7 +75,7 @@ func (suite *RegistryScopeTestSuite) Test_1_Cehck_Push_Request_Scope() { } -func (suite *RegistryScopeTestSuite) Test_2_Cehck_Pull_Request_Scope() { +func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { //set simple auth server to check the auth request scope server := &http.Server{ From 8c8d6e3262be77783ef42a47c9593b6e42a3066a Mon Sep 17 00:00:00 2001 From: kimsungmin1 Date: Thu, 11 Dec 2025 00:49:09 +0900 Subject: [PATCH 05/16] change client_scope_test.go to use httptest Signed-off-by: kimsungmin1 --- pkg/registry/client_scope_test.go | 61 ++++++++++++------------------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go index 37c77d7d3..e5a34214e 100644 --- a/pkg/registry/client_scope_test.go +++ b/pkg/registry/client_scope_test.go @@ -17,10 +17,10 @@ limitations under the License. package registry import ( - "context" "fmt" - "log" + "net" "net/http" + "net/http/httptest" "os" "testing" @@ -44,20 +44,17 @@ func (suite *RegistryScopeTestSuite) TearDownSuite() { func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { - //set simple auth server to check the auth request scope - server := &http.Server{ - Addr: suite.AuthServerHost, - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - suite.Equal(string("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice"), r.URL.String()) - w.WriteHeader(http.StatusOK) - }), - } - go func() { - err := server.ListenAndServe() - if err != nil && err != http.ErrServerClosed { - log.Fatalf("http server failed to ListenAndServe:%v", err) - } - }() + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.Equal(string("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice"), r.URL.String()) + w.WriteHeader(http.StatusOK) + }) + listener, err := net.Listen("tcp", suite.AuthServerHost) + suite.Nil(err, "no error creating server listner") + + ts := httptest.NewUnstartedServer(handler) + ts.Listener = listener + ts.Start() + defer ts.Close() // basic push, good ref testingChartCreationTime := "1977-09-02T22:04:05Z" @@ -69,28 +66,21 @@ func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { _, err = suite.RegistryClient.Push(chartData, ref, PushOptCreationTime(testingChartCreationTime)) suite.NotNil(err, "error pushing good ref because auth server don't give proper token") - //shutdown auth server - err = server.Shutdown(context.Background()) - suite.Nil(err, "shutdown simple auth server") - } func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { - //set simple auth server to check the auth request scope - server := &http.Server{ - Addr: suite.AuthServerHost, - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - suite.Equal(string("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull&service=testservice"), r.URL.String()) - w.WriteHeader(http.StatusOK) - }), - } - go func() { - err := server.ListenAndServe() - if err != nil && err != http.ErrServerClosed { - log.Fatalf("http server failed to ListenAndServe:%v", err) - } - }() + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + suite.Equal(string("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull&service=testservice"), r.URL.String()) + w.WriteHeader(http.StatusOK) + }) + listener, err := net.Listen("tcp", suite.AuthServerHost) + suite.Nil(err, "no error creating server listner") + + ts := httptest.NewUnstartedServer(handler) + ts.Listener = listener + ts.Start() + defer ts.Close() // Load test chart (to build ref pushed in previous test) // Simple pull, chart only @@ -102,9 +92,6 @@ func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { _, err = suite.RegistryClient.Pull(ref) suite.NotNil(err, "error pulling a simple chart because auth server don't give proper token") - //shutdown auth server - err = server.Shutdown(context.Background()) - suite.Nil(err, "shutdown simple auth server") } func TestRegistryScopeTestSuite(t *testing.T) { From 26e5071f6680272d773f0ca22ea946a35c4dce66 Mon Sep 17 00:00:00 2001 From: kimsungmin1 Date: Fri, 9 Jan 2026 20:10:24 +0900 Subject: [PATCH 06/16] change suite.Nil, suite.NotNill to more proper function(suite.NoError, suite.Error) Signed-off-by: kimsungmin1 --- pkg/registry/client_scope_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go index e5a34214e..2039faad9 100644 --- a/pkg/registry/client_scope_test.go +++ b/pkg/registry/client_scope_test.go @@ -49,7 +49,7 @@ func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { w.WriteHeader(http.StatusOK) }) listener, err := net.Listen("tcp", suite.AuthServerHost) - suite.Nil(err, "no error creating server listner") + suite.NoError(err, "no error creating server listner") ts := httptest.NewUnstartedServer(handler) ts.Listener = listener @@ -59,12 +59,12 @@ func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { // basic push, good ref testingChartCreationTime := "1977-09-02T22:04:05Z" chartData, err := os.ReadFile("../downloader/testdata/local-subchart-0.1.0.tgz") - suite.Nil(err, "no error loading test chart") + suite.NoError(err, "no error loading test chart") meta, err := extractChartMeta(chartData) - suite.Nil(err, "no error extracting chart meta") + suite.NoError(err, "no error extracting chart meta") ref := fmt.Sprintf("%s/testrepo/%s:%s", suite.DockerRegistryHost, meta.Name, meta.Version) _, err = suite.RegistryClient.Push(chartData, ref, PushOptCreationTime(testingChartCreationTime)) - suite.NotNil(err, "error pushing good ref because auth server don't give proper token") + suite.Error(err, "error pushing good ref because auth server don't give proper token") } @@ -75,7 +75,7 @@ func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { w.WriteHeader(http.StatusOK) }) listener, err := net.Listen("tcp", suite.AuthServerHost) - suite.Nil(err, "no error creating server listner") + suite.NoError(err, "no error creating server listner") ts := httptest.NewUnstartedServer(handler) ts.Listener = listener @@ -85,12 +85,12 @@ func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { // Load test chart (to build ref pushed in previous test) // Simple pull, chart only chartData, err := os.ReadFile("../downloader/testdata/local-subchart-0.1.0.tgz") - suite.Nil(err, "no error loading test chart") + suite.NoError(err, "no error loading test chart") meta, err := extractChartMeta(chartData) - suite.Nil(err, "no error extracting chart meta") + suite.NoError(err, "no error extracting chart meta") ref := fmt.Sprintf("%s/testrepo/%s:%s", suite.DockerRegistryHost, meta.Name, meta.Version) _, err = suite.RegistryClient.Pull(ref) - suite.NotNil(err, "error pulling a simple chart because auth server don't give proper token") + suite.Error(err, "error pulling a simple chart because auth server don't give proper token") } From 530e728ea932109befc4a8165cd9a2bc78170c07 Mon Sep 17 00:00:00 2001 From: kimsm28 Date: Wed, 11 Mar 2026 10:56:03 +0900 Subject: [PATCH 07/16] fix typo, remove unnecessary code, fix to avoid to use the assertion in http hanlder Signed-off-by: kimsm28 --- pkg/registry/client.go | 11 +++++++++-- pkg/registry/client_scope_test.go | 21 +++++++++++++++------ pkg/registry/registry_test.go | 1 - 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/pkg/registry/client.go b/pkg/registry/client.go index 366251c60..1fe4b7813 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -717,7 +717,7 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu repository.PlainHTTP = c.plainHTTP repository.Client = c.authorizer - ctx = WithScopeHint(ctx, repository, auth.ActionPush, auth.ActionPull) + ctx = withScopeHint(ctx, repository, auth.ActionPull, auth.ActionPush) manifestDescriptor, err = oras.ExtendedCopy(ctx, memoryStore, parsedRef.String(), repository, parsedRef.String(), oras.DefaultExtendedCopyOptions) if err != nil { @@ -930,7 +930,14 @@ func (c *Client) tagManifest(ctx context.Context, memoryStore *memory.Store, manifestData, parsedRef.String()) } -func WithScopeHint(ctx context.Context, target any, actions ...string) context.Context { +// add actions when request a registry authentication token(jwt) +// example1. when we want to pull 'testrepo/local-subchart' we can send bellow url, and 'pull' is the action +// auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull&service=testservice +// example2. when we want to push 'testrepo/local-subchart' we can send bellow url, and 'pull%2Cpush' are the actions +// auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice +// we can set the actions like bellow +// example) ctx = WithScopeHint(ctx, repository, auth.ActionPush, auth.ActionPull) +func withScopeHint(ctx context.Context, target any, actions ...string) context.Context { if repo, ok := target.(*remote.Repository); ok { return auth.AppendRepositoryScope(ctx, repo.Reference, actions...) } diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go index 2039faad9..2a3def6f6 100644 --- a/pkg/registry/client_scope_test.go +++ b/pkg/registry/client_scope_test.go @@ -44,12 +44,14 @@ func (suite *RegistryScopeTestSuite) TearDownSuite() { func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { + var requestUrl string + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - suite.Equal(string("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice"), r.URL.String()) + requestUrl = r.URL.String() w.WriteHeader(http.StatusOK) }) listener, err := net.Listen("tcp", suite.AuthServerHost) - suite.NoError(err, "no error creating server listner") + suite.NoError(err, "no error creating server listener") ts := httptest.NewUnstartedServer(handler) ts.Listener = listener @@ -64,18 +66,23 @@ func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { suite.NoError(err, "no error extracting chart meta") ref := fmt.Sprintf("%s/testrepo/%s:%s", suite.DockerRegistryHost, meta.Name, meta.Version) _, err = suite.RegistryClient.Push(chartData, ref, PushOptCreationTime(testingChartCreationTime)) - suite.Error(err, "error pushing good ref because auth server don't give proper token") + suite.Error(err, "error pushing good ref because auth server doesn't give proper token") + + //check the url that authentication server received + suite.Equal("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice", requestUrl) } func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { + var requestUrl string + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - suite.Equal(string("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull&service=testservice"), r.URL.String()) + requestUrl = r.URL.String() w.WriteHeader(http.StatusOK) }) listener, err := net.Listen("tcp", suite.AuthServerHost) - suite.NoError(err, "no error creating server listner") + suite.NoError(err, "no error creating server listener") ts := httptest.NewUnstartedServer(handler) ts.Listener = listener @@ -90,8 +97,10 @@ func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { suite.NoError(err, "no error extracting chart meta") ref := fmt.Sprintf("%s/testrepo/%s:%s", suite.DockerRegistryHost, meta.Name, meta.Version) _, err = suite.RegistryClient.Pull(ref) - suite.Error(err, "error pulling a simple chart because auth server don't give proper token") + suite.Error(err, "error pulling a simple chart because auth server doesn't give proper token") + //check the url that authentication server received + suite.Equal("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull&service=testservice", requestUrl) } func TestRegistryScopeTestSuite(t *testing.T) { diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index 5f745c8a1..3dc65908a 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -146,7 +146,6 @@ func setup(suite *TestRegistry, tlsEnabled, insecure bool, auth string) { if auth == "token" { ln, err := net.Listen("tcp", "127.0.0.1:0") suite.Nil(err, "no error finding free port for test auth server") - defer ln.Close() //set test auth server host suite.AuthServerHost = ln.Addr().String() From 8883c9cc7006eef7e3d07836b2d5dd7ca1066168 Mon Sep 17 00:00:00 2001 From: kimsm28 Date: Wed, 11 Mar 2026 11:03:34 +0900 Subject: [PATCH 08/16] fix variable naming requestUrl -> requestURL Signed-off-by: kimsm28 --- pkg/registry/client_scope_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go index 2a3def6f6..a5f8ea16c 100644 --- a/pkg/registry/client_scope_test.go +++ b/pkg/registry/client_scope_test.go @@ -44,10 +44,10 @@ func (suite *RegistryScopeTestSuite) TearDownSuite() { func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { - var requestUrl string + var requestURL string handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestUrl = r.URL.String() + requestURL = r.URL.String() w.WriteHeader(http.StatusOK) }) listener, err := net.Listen("tcp", suite.AuthServerHost) @@ -69,16 +69,16 @@ func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { suite.Error(err, "error pushing good ref because auth server doesn't give proper token") //check the url that authentication server received - suite.Equal("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice", requestUrl) + suite.Equal("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice", requestURL) } func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { - var requestUrl string + var requestURL string handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestUrl = r.URL.String() + requestURL = r.URL.String() w.WriteHeader(http.StatusOK) }) listener, err := net.Listen("tcp", suite.AuthServerHost) @@ -100,7 +100,7 @@ func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { suite.Error(err, "error pulling a simple chart because auth server doesn't give proper token") //check the url that authentication server received - suite.Equal("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull&service=testservice", requestUrl) + suite.Equal("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull&service=testservice", requestURL) } func TestRegistryScopeTestSuite(t *testing.T) { From 1f0a72849657c3fe167320f7f5729c377f6dadf8 Mon Sep 17 00:00:00 2001 From: kimsm28 Date: Mon, 13 Apr 2026 13:15:49 +0900 Subject: [PATCH 09/16] fix registry test failures by adjusting DockerRegistryHost and auth server listener management - Change DockerRegistryHost to use 127.0.0.1 for HTTP tests and helm-test-registry for TLS tests to match certificate hostname - Add defer ln.Close() to prevent resource leak in token auth server setup - Restore requestURL variable and assertion in test body instead of handler to avoid potential race condition Signed-off-by: kimsm28 --- pkg/registry/client_scope_test.go | 3 --- pkg/registry/registry_test.go | 12 ++++++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go index a5f8ea16c..09ad2b0d0 100644 --- a/pkg/registry/client_scope_test.go +++ b/pkg/registry/client_scope_test.go @@ -45,7 +45,6 @@ func (suite *RegistryScopeTestSuite) TearDownSuite() { func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { var requestURL string - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requestURL = r.URL.String() w.WriteHeader(http.StatusOK) @@ -70,13 +69,11 @@ func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { //check the url that authentication server received suite.Equal("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice", requestURL) - } func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { var requestURL string - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requestURL = r.URL.String() w.WriteHeader(http.StatusOK) diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index 3dc65908a..d00a9f3c6 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -133,11 +133,14 @@ func setup(suite *TestRegistry, tlsEnabled, insecure bool, auth string) { suite.Require().NoError(err, "no error finding free port for test registry") defer func() { _ = ln.Close() }() - // Change the registry host to another host which is not localhost. - // This is required because Docker enforces HTTP if the registry - // host is localhost/127.0.0.1. + // Use localhost for HTTP tests and helm-test-registry for TLS tests. + // TLS tests need a different hostname to match the certificate. port := ln.Addr().(*net.TCPAddr).Port - suite.DockerRegistryHost = fmt.Sprintf("helm-test-registry:%d", port) + if tlsEnabled { + suite.DockerRegistryHost = fmt.Sprintf("helm-test-registry:%d", port) + } else { + suite.DockerRegistryHost = fmt.Sprintf("127.0.0.1:%d", port) + } config.HTTP.Addr = ln.Addr().String() config.HTTP.DrainTimeout = time.Duration(10) * time.Second @@ -146,6 +149,7 @@ func setup(suite *TestRegistry, tlsEnabled, insecure bool, auth string) { if auth == "token" { ln, err := net.Listen("tcp", "127.0.0.1:0") suite.Nil(err, "no error finding free port for test auth server") + defer ln.Close() //set test auth server host suite.AuthServerHost = ln.Addr().String() From ee181f4570550d61379bf59f2a69b993b74d49cf Mon Sep 17 00:00:00 2001 From: kimsm28 Date: Mon, 13 Apr 2026 13:29:21 +0900 Subject: [PATCH 10/16] fix typos in withScopeHint function comment - Change 'bellow' to 'below' (spelling correction in multiple places) - Change 'WithScopeHint' to 'withScopeHint' to match actual function name Signed-off-by: kimsm28 --- pkg/registry/client.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/registry/client.go b/pkg/registry/client.go index 1fe4b7813..e252ad2c5 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -931,12 +931,12 @@ func (c *Client) tagManifest(ctx context.Context, memoryStore *memory.Store, } // add actions when request a registry authentication token(jwt) -// example1. when we want to pull 'testrepo/local-subchart' we can send bellow url, and 'pull' is the action +// example1. when we want to pull 'testrepo/local-subchart' we can send below url, and 'pull' is the action // auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull&service=testservice -// example2. when we want to push 'testrepo/local-subchart' we can send bellow url, and 'pull%2Cpush' are the actions +// example2. when we want to push 'testrepo/local-subchart' we can send below url, and 'pull%2Cpush' are the actions // auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice -// we can set the actions like bellow -// example) ctx = WithScopeHint(ctx, repository, auth.ActionPush, auth.ActionPull) +// we can set the actions like below +// example) ctx = withScopeHint(ctx, repository, auth.ActionPush, auth.ActionPull) func withScopeHint(ctx context.Context, target any, actions ...string) context.Context { if repo, ok := target.(*remote.Repository); ok { return auth.AppendRepositoryScope(ctx, repo.Reference, actions...) From 9b7a70f0ca4bf0dcfd242962d9eef211188057dd Mon Sep 17 00:00:00 2001 From: kimsm28 Date: Mon, 13 Apr 2026 17:53:48 +0900 Subject: [PATCH 11/16] test: improve client_scope_test.go to avoid data races and brittle assertions - Fix data race by using channel instead of shared variable for requestURL - Make assertions more robust by parsing URL and checking query parameters instead of exact string matching Signed-off-by: kimsm28 --- pkg/registry/client_scope_test.go | 36 +++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go index 09ad2b0d0..b1da20a6b 100644 --- a/pkg/registry/client_scope_test.go +++ b/pkg/registry/client_scope_test.go @@ -21,8 +21,10 @@ import ( "net" "net/http" "net/http/httptest" + "net/url" "os" "testing" + "time" "github.com/stretchr/testify/suite" ) @@ -44,9 +46,9 @@ func (suite *RegistryScopeTestSuite) TearDownSuite() { func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { - var requestURL string + requestURL := make(chan string, 1) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestURL = r.URL.String() + requestURL <- r.URL.String() w.WriteHeader(http.StatusOK) }) listener, err := net.Listen("tcp", suite.AuthServerHost) @@ -68,14 +70,25 @@ func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { suite.Error(err, "error pushing good ref because auth server doesn't give proper token") //check the url that authentication server received - suite.Equal("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice", requestURL) + select { + case urlStr := <-requestURL: + u, err := url.Parse(urlStr) + suite.NoError(err, "no error parsing requested URL") + + suite.Equal("/auth", u.Path) + suite.Equal("testservice", u.Query().Get("service")) + scope := u.Query().Get("scope") + suite.Contains(scope, "repository:testrepo/local-subchart:pull,push") + case <-time.After(5 * time.Second): + suite.T().Fatal("timeout waiting for auth request") + } } func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { - var requestURL string + requestURL := make(chan string, 1) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestURL = r.URL.String() + requestURL <- r.URL.String() w.WriteHeader(http.StatusOK) }) listener, err := net.Listen("tcp", suite.AuthServerHost) @@ -97,7 +110,18 @@ func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { suite.Error(err, "error pulling a simple chart because auth server doesn't give proper token") //check the url that authentication server received - suite.Equal("/auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull&service=testservice", requestURL) + select { + case urlStr := <-requestURL: + u, err := url.Parse(urlStr) + suite.NoError(err, "no error parsing requested URL") + + suite.Equal("/auth", u.Path) + suite.Equal("testservice", u.Query().Get("service")) + scope := u.Query().Get("scope") + suite.Contains(scope, "repository:testrepo/local-subchart:pull") + case <-time.After(5 * time.Second): + suite.T().Fatal("timeout waiting for auth request") + } } func TestRegistryScopeTestSuite(t *testing.T) { From 26ada49c1ad50af5e11dbb8a65b703251295c689 Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Mon, 25 May 2026 08:45:09 -0600 Subject: [PATCH 12/16] Update pkg/registry/client.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Terry Howe --- pkg/registry/client.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pkg/registry/client.go b/pkg/registry/client.go index e252ad2c5..4d7da78dc 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -937,9 +937,6 @@ func (c *Client) tagManifest(ctx context.Context, memoryStore *memory.Store, // auth?scope=repository%3Atestrepo%2Flocal-subchart%3Apull%2Cpush&service=testservice // we can set the actions like below // example) ctx = withScopeHint(ctx, repository, auth.ActionPush, auth.ActionPull) -func withScopeHint(ctx context.Context, target any, actions ...string) context.Context { - if repo, ok := target.(*remote.Repository); ok { - return auth.AppendRepositoryScope(ctx, repo.Reference, actions...) - } - return ctx +func withScopeHint(ctx context.Context, repo *remote.Repository, actions ...string) context.Context { + return auth.AppendRepositoryScope(ctx, repo.Reference, actions...) } From 3cbee7e935eb6e783c36b17d6eb79eb6edcda15b Mon Sep 17 00:00:00 2001 From: kimsm28 Date: Thu, 2 Jul 2026 21:49:33 +0900 Subject: [PATCH 13/16] fix(registry): use plain-http registry in token-auth scope test The scope test configured a TLS registry while advertising an http token realm. oras refuses to send credentials to an http token realm when the registry itself was contacted over https, so the auth server was never reached and the test timed out ("timeout waiting for auth request"). Use a plain-http registry so the registry and token-realm schemes match; the scope carried in the token request is what this test verifies, and that is independent of TLS. Also make the auth handler's channel send non-blocking so that a client retry can never block the handler and stall the push/pull flow, which addresses the review comment about a potential deadlock. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: kimsm28 --- pkg/registry/client_scope_test.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go index b1da20a6b..98cdc74ff 100644 --- a/pkg/registry/client_scope_test.go +++ b/pkg/registry/client_scope_test.go @@ -30,17 +30,18 @@ import ( ) type RegistryScopeTestSuite struct { - TestSuite + TestRegistry } func (suite *RegistryScopeTestSuite) SetupSuite() { - // set registry use token auth - dockerRegistry := setup(&suite.TestSuite, true, true, "token") - // Start Docker registry - go dockerRegistry.ListenAndServe() + // Set up a plain-HTTP registry that uses token auth. The token realm is + // served over http (see setup), so the registry must be contacted over + // http as well: oras refuses to send credentials to an http token realm + // when the registry itself was reached over https. + setup(&suite.TestRegistry, false, false, "token") } func (suite *RegistryScopeTestSuite) TearDownSuite() { - teardown(&suite.TestSuite) + teardown(&suite.TestRegistry) os.RemoveAll(suite.WorkspaceDir) } @@ -48,7 +49,13 @@ func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { requestURL := make(chan string, 1) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestURL <- r.URL.String() + // Capture only the first auth request; never block the handler if the + // client happens to retry, so the auth server always responds and the + // push/pull flow can't deadlock waiting on us. + select { + case requestURL <- r.URL.String(): + default: + } w.WriteHeader(http.StatusOK) }) listener, err := net.Listen("tcp", suite.AuthServerHost) @@ -88,7 +95,13 @@ func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { requestURL := make(chan string, 1) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestURL <- r.URL.String() + // Capture only the first auth request; never block the handler if the + // client happens to retry, so the auth server always responds and the + // push/pull flow can't deadlock waiting on us. + select { + case requestURL <- r.URL.String(): + default: + } w.WriteHeader(http.StatusOK) }) listener, err := net.Listen("tcp", suite.AuthServerHost) From 4e9ca06856b1aeba65c4d4cd8e4dce4c83445776 Mon Sep 17 00:00:00 2001 From: kimsm28 Date: Thu, 2 Jul 2026 21:49:33 +0900 Subject: [PATCH 14/16] chore: go mod tidy after rebase on main Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: kimsm28 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ca3d23bb2..628b3f374 100644 --- a/go.mod +++ b/go.mod @@ -80,7 +80,7 @@ require ( github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect - github.com/go-jose/go-jose/v4 v4.0.5 // indirect + github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect diff --git a/go.sum b/go.sum index 2d8184ab0..dc469fde9 100644 --- a/go.sum +++ b/go.sum @@ -105,8 +105,8 @@ github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8b github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= -github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= -github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= From 503acff97580aa803f5d639fb1fbb06ad1226c31 Mon Sep 17 00:00:00 2001 From: kimsm28 Date: Thu, 2 Jul 2026 21:57:38 +0900 Subject: [PATCH 15/16] fix(registry): resolve golangci-lint issues in token-auth tests - Use (*net.ListenConfig).Listen with the test context instead of net.Listen (noctx). - Use suite.NoError instead of suite.Nil for the error check (testifylint). - Drop the unnecessary leading blank lines in the scope test funcs (whitespace). Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: kimsm28 --- pkg/registry/client_scope_test.go | 8 ++++---- pkg/registry/registry_test.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/registry/client_scope_test.go b/pkg/registry/client_scope_test.go index 98cdc74ff..f5f32ecb1 100644 --- a/pkg/registry/client_scope_test.go +++ b/pkg/registry/client_scope_test.go @@ -46,7 +46,6 @@ func (suite *RegistryScopeTestSuite) TearDownSuite() { } func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { - requestURL := make(chan string, 1) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Capture only the first auth request; never block the handler if the @@ -58,7 +57,8 @@ func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { } w.WriteHeader(http.StatusOK) }) - listener, err := net.Listen("tcp", suite.AuthServerHost) + lnCfg := net.ListenConfig{} + listener, err := lnCfg.Listen(suite.T().Context(), "tcp", suite.AuthServerHost) suite.NoError(err, "no error creating server listener") ts := httptest.NewUnstartedServer(handler) @@ -92,7 +92,6 @@ func (suite *RegistryScopeTestSuite) Test_1_Check_Push_Request_Scope() { } func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { - requestURL := make(chan string, 1) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Capture only the first auth request; never block the handler if the @@ -104,7 +103,8 @@ func (suite *RegistryScopeTestSuite) Test_2_Check_Pull_Request_Scope() { } w.WriteHeader(http.StatusOK) }) - listener, err := net.Listen("tcp", suite.AuthServerHost) + lnCfg := net.ListenConfig{} + listener, err := lnCfg.Listen(suite.T().Context(), "tcp", suite.AuthServerHost) suite.NoError(err, "no error creating server listener") ts := httptest.NewUnstartedServer(handler) diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index d00a9f3c6..9388c8142 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -147,8 +147,8 @@ func setup(suite *TestRegistry, tlsEnabled, insecure bool, auth string) { config.Storage = map[string]configuration.Parameters{"inmemory": map[string]any{}} if auth == "token" { - ln, err := net.Listen("tcp", "127.0.0.1:0") - suite.Nil(err, "no error finding free port for test auth server") + ln, err := lnCfg.Listen(suite.T().Context(), "tcp", "127.0.0.1:0") + suite.NoError(err, "no error finding free port for test auth server") defer ln.Close() //set test auth server host From d539556a8d03c48258d08207aaf918c202c90df6 Mon Sep 17 00:00:00 2001 From: kimsungmin1 <142377392+kimsungmin1@users.noreply.github.com> Date: Fri, 3 Jul 2026 21:28:56 +0900 Subject: [PATCH 16/16] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: kimsungmin1 <142377392+kimsungmin1@users.noreply.github.com> --- pkg/registry/registry_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index 9388c8142..7d7b5a112 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -148,7 +148,7 @@ func setup(suite *TestRegistry, tlsEnabled, insecure bool, auth string) { if auth == "token" { ln, err := lnCfg.Listen(suite.T().Context(), "tcp", "127.0.0.1:0") - suite.NoError(err, "no error finding free port for test auth server") + suite.Require().NoError(err, "no error finding free port for test auth server") defer ln.Close() //set test auth server host