From 615673bf1eb633e9df76ecdf27d6f0928c3a0f14 Mon Sep 17 00:00:00 2001 From: Diego Balseiro Date: Mon, 26 Jan 2026 13:26:48 -0500 Subject: [PATCH 1/4] ACR Login: Use Azure SDK library to get the credential and token --- pkg/k8s/keychains.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/k8s/keychains.go b/pkg/k8s/keychains.go index 7e65ceb8f3..768f2f8d6e 100644 --- a/pkg/k8s/keychains.go +++ b/pkg/k8s/keychains.go @@ -54,7 +54,7 @@ func GetACRCredentialLoader() []creds.CredentialsCallback { if !strings.HasSuffix(registry, ".azurecr.io") { return oci.Credentials{}, creds.ErrCredentialsNotFound } - + // TODO: Use Azure SDK to get access token instead of reading from file f, err := os.Open(path.Join(os.Getenv("HOME"), ".azure", "accessTokens.json")) if err != nil { return oci.Credentials{}, fmt.Errorf("open Azure access tokens: %w", err) From 9aa595b77a4d857302c03817b569d95290364cc7 Mon Sep 17 00:00:00 2001 From: Diego Balseiro Date: Tue, 3 Feb 2026 17:02:29 -0500 Subject: [PATCH 2/4] Add Azure SDK and implement ACR Login --- go.mod | 7 +++++++ go.sum | 12 ++++++++++++ pkg/k8s/keychains.go | 42 ++++++++++++++++-------------------------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 5906e194ae..4e82507011 100644 --- a/go.mod +++ b/go.mod @@ -83,6 +83,9 @@ require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect dario.cat/mergo v1.0.2 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.30 // indirect @@ -92,6 +95,7 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.2 // indirect github.com/Azure/go-autorest/tracing v0.6.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/GoogleContainerTools/kaniko v1.24.0 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Microsoft/hcsshim v0.13.0 // indirect @@ -167,6 +171,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect @@ -199,6 +204,7 @@ require ( github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -239,6 +245,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect diff --git a/go.sum b/go.sum index 289181e8ce..609e597fd9 100644 --- a/go.sum +++ b/go.sum @@ -65,6 +65,12 @@ github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkk github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= @@ -96,6 +102,8 @@ github.com/Azure/go-autorest/logger v0.2.2/go.mod h1:I5fg9K52o+iuydlWfa9T5K6WFos github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-autorest/tracing v0.6.1 h1:YUMSrC/CeD1ZnnXcNYU4a/fzsO35u2Fsful9L/2nyR0= github.com/Azure/go-autorest/tracing v0.6.1/go.mod h1:/3EgjbsjraOqiicERAeu3m7/z0x1TzjQGAwDrJrXGkc= +github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= @@ -464,6 +472,8 @@ github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -888,6 +898,8 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/pkg/k8s/keychains.go b/pkg/k8s/keychains.go index 768f2f8d6e..0cff567aa7 100644 --- a/pkg/k8s/keychains.go +++ b/pkg/k8s/keychains.go @@ -1,12 +1,12 @@ package k8s import ( - "encoding/json" + "context" "fmt" - "os" - "path" "strings" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/google" @@ -54,32 +54,22 @@ func GetACRCredentialLoader() []creds.CredentialsCallback { if !strings.HasSuffix(registry, ".azurecr.io") { return oci.Credentials{}, creds.ErrCredentialsNotFound } - // TODO: Use Azure SDK to get access token instead of reading from file - f, err := os.Open(path.Join(os.Getenv("HOME"), ".azure", "accessTokens.json")) + // Use Azure SDK to get access token + azCredentials, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { - return oci.Credentials{}, fmt.Errorf("open Azure access tokens: %w", err) + return oci.Credentials{}, fmt.Errorf("failed to create default azure credentials: %w", err) } - defer f.Close() - - var tokens []struct { - AccessToken string `json:"accessToken"` - Resource string `json:"resource"` - } - - if err := json.NewDecoder(f).Decode(&tokens); err != nil { - return oci.Credentials{}, fmt.Errorf("decode Azure access tokens: %w", err) - } - - target := "https://" + registry - for _, t := range tokens { - if t.Resource == target { - return oci.Credentials{ - Username: "00000000-0000-0000-0000-000000000000", - Password: t.AccessToken, - }, nil - } + scope := "https://containerregistry.azure.net/.default" + token, err := azCredentials.GetToken(context.Background(), policy.TokenRequestOptions{ + Scopes: []string{scope}, + }) + if err != nil { + return oci.Credentials{}, fmt.Errorf("failed to get azure access token: %w", err) } - return oci.Credentials{}, creds.ErrCredentialsNotFound + return oci.Credentials{ + Username: "00000000-0000-0000-0000-000000000000", + Password: token.Token, + }, nil }, } } From 8ebe95c502cc73efb41b701d9447a8b0662ed196 Mon Sep 17 00:00:00 2001 From: Diego Balseiro Date: Tue, 10 Feb 2026 12:50:58 -0500 Subject: [PATCH 3/4] run update-codegen --- go.mod | 4 ++-- go.sum | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 4e82507011..0c396aba71 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,8 @@ replace knative.dev/pkg => knative.dev/pkg v0.0.0-20250716115900-19d3cc2da0b9 require ( github.com/AlecAivazis/survey/v2 v2.3.7 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 github.com/BurntSushi/toml v1.5.0 github.com/Masterminds/semver v1.5.0 github.com/Microsoft/go-winio v0.6.2 @@ -83,8 +85,6 @@ require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect dario.cat/mergo v1.0.2 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect diff --git a/go.sum b/go.sum index 609e597fd9..d479a00330 100644 --- a/go.sum +++ b/go.sum @@ -69,6 +69,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16AP github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= @@ -102,6 +104,8 @@ github.com/Azure/go-autorest/logger v0.2.2/go.mod h1:I5fg9K52o+iuydlWfa9T5K6WFos github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-autorest/tracing v0.6.1 h1:YUMSrC/CeD1ZnnXcNYU4a/fzsO35u2Fsful9L/2nyR0= github.com/Azure/go-autorest/tracing v0.6.1/go.mod h1:/3EgjbsjraOqiicERAeu3m7/z0x1TzjQGAwDrJrXGkc= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -692,6 +696,8 @@ github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dv github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= +github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= From 6609dd815fdb732127d4bca960b62288c8974861 Mon Sep 17 00:00:00 2001 From: Diego Balseiro Date: Fri, 13 Mar 2026 20:37:56 -0500 Subject: [PATCH 4/4] Unify credential loader interface with context support; add ACR tests --- cmd/client.go | 4 +++- pkg/creds/credentials.go | 41 ++++++++++++++++++++++++++++++++++----- pkg/k8s/keychains.go | 8 ++++---- pkg/k8s/keychains_test.go | 20 +++++++++++++++++++ 4 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 pkg/k8s/keychains_test.go diff --git a/cmd/client.go b/cmd/client.go index 9dea30f629..b43b59de0c 100644 --- a/cmd/client.go +++ b/cmd/client.go @@ -108,7 +108,6 @@ func newTransport(insecureSkipVerify bool) fnhttp.RoundTripCloser { func newCredentialsProvider(configPath string, t http.RoundTripper, authFilePath string) oci.CredentialsProvider { additionalLoaders := append(k8s.GetOpenShiftDockerCredentialLoaders(), k8s.GetGoogleCredentialLoader()...) additionalLoaders = append(additionalLoaders, k8s.GetECRCredentialLoader()...) - additionalLoaders = append(additionalLoaders, k8s.GetACRCredentialLoader()...) additionalLoaders = append(additionalLoaders, func(registry string) (oci.Credentials, error) { @@ -126,11 +125,14 @@ func newCredentialsProvider(configPath string, t http.RoundTripper, authFilePath }, ) + contextLoaders := k8s.GetACRCredentialLoader() + options := []creds.Opt{ creds.WithPromptForCredentials(prompt.NewPromptForCredentials(os.Stdin, os.Stdout, os.Stderr)), creds.WithPromptForCredentialStore(prompt.NewPromptForCredentialStore()), creds.WithTransport(t), creds.WithAdditionalCredentialLoaders(additionalLoaders...), + creds.WithContextCredentialLoaders(contextLoaders...), } // If a custom auth file path is provided, use it diff --git a/pkg/creds/credentials.go b/pkg/creds/credentials.go index 4502a80a80..380d585b3a 100644 --- a/pkg/creds/credentials.go +++ b/pkg/creds/credentials.go @@ -28,6 +28,10 @@ import ( type CredentialsCallback func(registry string) (oci.Credentials, error) +// ContextCredentialsCallback represents a credential retrieval callback that supports context for cancellation and timeouts. +// It should return ErrCredentialsNotFound if no credentials are available for the given registry. +type ContextCredentialsCallback func(ctx context.Context, registry string) (oci.Credentials, error) + var ErrUnauthorized = errors.New("bad credentials") var ErrCredentialsNotFound = errors.New("credentials not found") @@ -88,6 +92,7 @@ type credentialsProvider struct { verifyCredentials VerifyCredentialsCallback promptForCredentialStore ChooseCredentialHelperCallback credentialLoaders []CredentialsCallback + contextCredentialLoaders []ContextCredentialsCallback authFilePath string transport http.RoundTripper } @@ -146,6 +151,22 @@ func WithAdditionalCredentialLoaders(loaders ...CredentialsCallback) Opt { } } +// WithContextCredentialLoaders adds custom context-aware callbacks for credential retrieval. +// These callbacks accept context for cancellation and timeout support, +// and must return ErrCredentialsNotFound if the credentials are not found. +// The callbacks are intended to be non-interactive, as opposed to WithPromptForCredentials. +// +// This is particularly useful when credential retrieval may need to be interrupted +// (for example, due to network delays, API timeouts, or user cancel actions). +// +// Example usage: Azure Container Registry loader supports context cancellation +// for credential acquisition routines. +func WithContextCredentialLoaders(loaders ...ContextCredentialsCallback) Opt { + return func(opts *credentialsProvider) { + opts.contextCredentialLoaders = append(opts.contextCredentialLoaders, loaders...) + } +} + // NewCredentialsProvider returns new CredentialsProvider that tries to get credentials from docker/func config files. // // In case getting credentials from the config files fails @@ -261,6 +282,19 @@ func NewCredentialsProvider(configPath string, opts ...Opt) oci.CredentialsProvi return c.getCredentials } +func (c *credentialsProvider) getAllCredentialLoaders() []ContextCredentialsCallback { + // Unify all callbacks into a single slice + var allLoaders []ContextCredentialsCallback + // Wrap non-context loaders to match the ContextCredentialsCallback signature + for _, load := range c.credentialLoaders { + allLoaders = append(allLoaders, func(ctx context.Context, registry string) (oci.Credentials, error) { + return load(registry) + }) + } + allLoaders = append(allLoaders, c.contextCredentialLoaders...) + return allLoaders +} + func (c *credentialsProvider) getCredentials(ctx context.Context, image string) (oci.Credentials, error) { var err error result := oci.Credentials{} @@ -271,10 +305,8 @@ func (c *credentialsProvider) getCredentials(ctx context.Context, image string) } registry := ref.Context().RegistryStr() - for _, load := range c.credentialLoaders { - - result, err = load(registry) - + for _, load := range c.getAllCredentialLoaders() { + result, err = load(ctx, registry) if err != nil { if errors.Is(err, ErrCredentialsNotFound) { continue @@ -290,7 +322,6 @@ func (c *credentialsProvider) getCredentials(ctx context.Context, image string) return oci.Credentials{}, err } } - } if c.promptForCredentials == nil { diff --git a/pkg/k8s/keychains.go b/pkg/k8s/keychains.go index 0cff567aa7..5968b29b58 100644 --- a/pkg/k8s/keychains.go +++ b/pkg/k8s/keychains.go @@ -48,9 +48,9 @@ func GetECRCredentialLoader() []creds.CredentialsCallback { return []creds.CredentialsCallback{} // TODO: Implement ECR credentials loader } -func GetACRCredentialLoader() []creds.CredentialsCallback { - return []creds.CredentialsCallback{ - func(registry string) (oci.Credentials, error) { +func GetACRCredentialLoader() []creds.ContextCredentialsCallback { + return []creds.ContextCredentialsCallback{ + func(ctx context.Context, registry string) (oci.Credentials, error) { if !strings.HasSuffix(registry, ".azurecr.io") { return oci.Credentials{}, creds.ErrCredentialsNotFound } @@ -60,7 +60,7 @@ func GetACRCredentialLoader() []creds.CredentialsCallback { return oci.Credentials{}, fmt.Errorf("failed to create default azure credentials: %w", err) } scope := "https://containerregistry.azure.net/.default" - token, err := azCredentials.GetToken(context.Background(), policy.TokenRequestOptions{ + token, err := azCredentials.GetToken(ctx, policy.TokenRequestOptions{ Scopes: []string{scope}, }) if err != nil { diff --git a/pkg/k8s/keychains_test.go b/pkg/k8s/keychains_test.go new file mode 100644 index 0000000000..8f5f005081 --- /dev/null +++ b/pkg/k8s/keychains_test.go @@ -0,0 +1,20 @@ +package k8s + +import ( + "context" + "testing" +) + +func TestACRCredentialLoader_ContextCancellation(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + loader := GetACRCredentialLoader()[0] + + registry := "example.azurecr.io" + + _, err := loader(ctx, registry) + if err == nil { + t.Fatal("expected error due to context cancellation, got nil") + } + t.Logf("Successfully caught cancellation error: %v", err) +}