@@ -18,7 +18,6 @@ package credentialprovider
1818
1919import (
2020 "context"
21- "errors"
2221 "fmt"
2322 "regexp"
2423 "strings"
@@ -67,29 +66,56 @@ type acrProvider struct {
6766 registryMirror map [string ]string // Registry mirror relation: source registry -> target registry
6867}
6968
70- func NewAcrProvider (config * providerconfig.AzureClientConfig , environment * azclient.Environment , credential azcore.TokenCredential ) CredentialProvider {
71- return & acrProvider {
72- config : config ,
73- credential : credential ,
74- environment : environment ,
75- }
76- }
69+ type getTokenCredentialFunc func (req * v1.CredentialProviderRequest , config * providerconfig.AzureClientConfig ) (azcore.TokenCredential , error )
7770
78- // NewAcrProvider creates a new instance of the ACR provider.
79- func NewAcrProviderFromConfig (configFile string , registryMirrorStr string ) (CredentialProvider , error ) {
80- if len (configFile ) == 0 {
81- return nil , errors .New ("no azure credential file is provided" )
82- }
71+ func NewAcrProvider (req * v1.CredentialProviderRequest , registryMirrorStr string , configFile string ) (CredentialProvider , error ) {
8372 config , err := configloader .Load [providerconfig.AzureClientConfig ](context .Background (), nil , & configloader.FileLoaderConfig {FilePath : configFile })
8473 if err != nil {
8574 return nil , fmt .Errorf ("failed to load config: %w" , err )
8675 }
8776
77+ _ , env , err := azclient .GetAzCoreClientOption (& config .ARMClientConfig )
78+ if err != nil {
79+ return nil , err
80+ }
81+ clientOption , _ , err := azclient .GetAzCoreClientOption (& config .ARMClientConfig )
82+ if err != nil {
83+ return nil , err
84+ }
85+
86+ var getTokenCredential getTokenCredentialFunc
87+
88+ // kubelet is responsible for checking the service account token emptiness when service account token is enabled, and only when service account token provide is enabled,
89+ // service account token is set in the request, so we can safely check the service account token emptiness to decide which credential to use.
90+ if len (req .ServiceAccountToken ) != 0 {
91+ klog .V (2 ).Infof ("Using service account token to authenticate ACR for image %s" , req .Image )
92+ getTokenCredential = getServiceAccountTokenCredential
93+ } else {
94+ klog .V (2 ).Infof ("Using managed identity to authenticate ACR for image %s" , req .Image )
95+ getTokenCredential = getManagedIdentityCredential
96+ }
97+ credential , err := getTokenCredential (req , config )
98+ if err != nil {
99+ return nil , fmt .Errorf ("failed to get token credential for image %s: %w" , req .Image , err )
100+ }
101+
102+ return & acrProvider {
103+ config : config ,
104+ credential : credential ,
105+ environment : env ,
106+ cloudConfig : clientOption .Cloud ,
107+ registryMirror : parseRegistryMirror (registryMirrorStr ),
108+ }, nil
109+ }
110+
111+ // getManagedIdentityCredential creates a new instance of the ACR provider.
112+ func getManagedIdentityCredential (_ * v1.CredentialProviderRequest , config * providerconfig.AzureClientConfig ) (azcore.TokenCredential , error ) {
113+
88114 var managedIdentityCredential azcore.TokenCredential
89115
90- clientOption , env , err := azclient .GetAzCoreClientOption (& config .ARMClientConfig )
116+ clientOption , _ , err := azclient .GetAzCoreClientOption (& config .ARMClientConfig )
91117 if err != nil {
92- return nil , err
118+ return nil , fmt . Errorf ( "Failed to get Azure client options: %w" , err )
93119 }
94120 if config .UseManagedIdentityExtension {
95121 credOptions := & azidentity.ManagedIdentityCredentialOptions {
@@ -108,13 +134,45 @@ func NewAcrProviderFromConfig(configFile string, registryMirrorStr string) (Cred
108134 }
109135 }
110136
111- return & acrProvider {
112- config : config ,
113- credential : managedIdentityCredential ,
114- environment : env ,
115- cloudConfig : clientOption .Cloud ,
116- registryMirror : parseRegistryMirror (registryMirrorStr ),
117- }, nil
137+ return managedIdentityCredential , nil
138+ }
139+
140+ func getServiceAccountTokenCredential (req * v1.CredentialProviderRequest , config * providerconfig.AzureClientConfig ) (azcore.TokenCredential , error ) {
141+ if len (req .ServiceAccountToken ) == 0 {
142+ return nil , fmt .Errorf ("kubernetes Service account token is not provided for image %s" , req .Image )
143+ }
144+ klog .V (2 ).Infof ("Kubernetes Service account token is provided for image %s" , req .Image )
145+
146+ clientOption , _ , err := azclient .GetAzCoreClientOption (& config .ARMClientConfig )
147+ if err != nil {
148+ return nil , fmt .Errorf ("Failed to get Azure client options for image %s: %w" , req .Image , err )
149+ }
150+
151+ // check required annotations
152+ clientID , ok := req .ServiceAccountAnnotations [clientIDAnnotation ]
153+ if ! ok || len (clientID ) == 0 {
154+ return nil , fmt .Errorf ("client id annotation %s is not found or the value is empty" , clientIDAnnotation )
155+ }
156+
157+ // check required annotations
158+ tenantID , ok := req .ServiceAccountAnnotations [tenantIDAnnotation ]
159+ if ! ok || len (tenantID ) == 0 {
160+ return nil , fmt .Errorf ("tenant id annotation %s is not found or the value is empty" , tenantIDAnnotation )
161+ }
162+
163+ // Create getAssertion callback that returns the service account token
164+ getAssertion := func (_ context.Context ) (string , error ) {
165+ return req .ServiceAccountToken , nil
166+ }
167+
168+ clientAssertCredential , err := azidentity .NewClientAssertionCredential (tenantID , clientID , getAssertion , & azidentity.ClientAssertionCredentialOptions {
169+ ClientOptions : * clientOption ,
170+ })
171+
172+ if err != nil {
173+ return nil , fmt .Errorf ("failed to initialize client assertion credential: %w" , err )
174+ }
175+ return clientAssertCredential , nil
118176}
119177
120178func (a * acrProvider ) GetCredentials (ctx context.Context , image string , _ []string ) (* v1.CredentialProviderResponse , error ) {
0 commit comments