@@ -18,16 +18,21 @@ package scope
1818
1919import (
2020 "context"
21- "encoding/base64"
21+ "os"
22+ "reflect"
2223 "testing"
2324
25+ "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
26+ "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
2427 . "github.com/onsi/gomega"
28+ "go.uber.org/mock/gomock"
2529 corev1 "k8s.io/api/core/v1"
2630 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2731 "k8s.io/apimachinery/pkg/runtime"
2832 "sigs.k8s.io/controller-runtime/pkg/client/fake"
2933
3034 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
35+ "sigs.k8s.io/cluster-api-provider-azure/azure/mock_azure"
3136)
3237
3338func TestAllowedNamespaces (t * testing.T ) {
@@ -202,20 +207,15 @@ func TestHasClientSecret(t *testing.T) {
202207}
203208
204209func TestGetTokenCredential (t * testing.T ) {
205- g := NewWithT (t )
206-
207- // Test cert data was generated with this command:
208- // openssl req -x509 -noenc -days 3650 -newkey rsa:2048 --keyout - -subj /CN=localhost | base64
209- encodedCertData := "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRGpyZEVyOVAwVGFVRVMKZHNwRTZjeW8yMk5VOHloUnJiWWxWOVZIMnZXdm5Qc1RoWGN4aG5kK2NVcWRORUJzd2h3Z0ZsVVFjZy9lU1Z4dwpyciszbmgrYkZUWldQY1krMUxRWXhmcEtHc3JDWFFmQjgyTERKSVpEWDRnSFlyV2YzWjI3MmpYTjFYZUZBS3RpCndES2dEWFh1UEg3cjVsSDd2QzNSWGVBZmZxTHdRSmhaZitOb0hOdHY5TUg5SWRVa1FmbURGWnRJL0NRekNyYjYKK3ZPUzZFbVVEL1EyRk5IQnpneENndUdxZ055QmNRYnhKOVFuZytaaklGdWhHWVhKbHN5UlV0ZXh5elRSNS92MApWTks4VXNaZ1JCRmhYcXJCdi9Sb0NDRyt4VkpZdG1kMFFzcnZOekRxRzZRbmpVQjIxelZYcXpLRWtXMmdSdGpYCmN3NHZZUWVoQWdNQkFBRUNnZ0VBUzZ4dGpnMG5Bb2trMGpTK1pPcEtsa01aQUZhemEzWnZ5SGlwa0hEejRQTXQKdGw3UmI1b1FaR3ZXVDJyYkVPcnhleTdCQmk3TEhHaEl1OEV4UXAvaFJHUG9CQUVUUDdYbHlDZ2hXUGtQdEV0RQpkVS9tWHhMb04wTnN6SHVmLzJzaTdwbUg4WXFHWjZRQjB0Z3IyMnV0NjBtYksrQUpGc0VFZjRhU3BCVXNwZXBKCjI4MDBzUUhzcVBFNkw2a1lrZloyR1JSWTFWOXZVcllFT0RLWnBXek1oTjNVQTluQUtIOVBCNnh2UDJPZHlNTmgKaEtnbVVVTU5JRnR3cjhwWmxKbjYwY2YwVXJXcmM1Q3ZxUUx1YUdZbHpEZ1VRR1Y0SkVWanFtOUY2bE1mRVBVdwplTjcwTVZlMXBjTGVMcTJyR0NWV1UzZ2FraC9IdkpxbFIvc2E1NDZIZ3dLQmdRRHlmMXZreVg0dzVzYm9pNkRKCmNsNWRNVUx0TU1ScEIxT2FNRlZPSmpJOWdaSjhtQ2RSanFYZFlvNWFTMktJcXhpZTh0R0c5K1NvaHhEQVdsNHQKbFNVdERzRTQ0ZlNtSUxxQzV6SWF3TlJRbm5rdjBYOEx3bVl1MFFkN1lBakpNbExUV3lEUnNqRDlYUnE0bnNSKwptSlZ3cnQ4NWlTcFM1VUZ5cnlFelBiRmowd0tCZ1FEd1d6cmFlTjBFY2NmMWlJWW1Rc1l5K3lNRUFsSE5SNXlpCmdQWHVBaFN5YnYySlJlUmhkVWIzOWhMci9Mdkt3MFplWGlMV1htWVVHcGJ5elB5WEltMHMrUEwzTFdsNjVHVEYKbCtjZlY1d2ZBZERrazZyQWRFUEVFMnB4Tjg1Q2h5YVBZUG9ZcjBvaG1WOTdWUWNZYzVGcVkrajF0TTZSMVJEdAovZldCU2E4aU93S0JnUUNwYTFkdFdXVERqNGdxVWRyc3d1MndtRWtVNDd4bFVJd1ZMbTE2NHU2NHovemk5WDZLCjJXbUNhV2ZoSjhmWWlnanlpOXpkT2ZYVDFFRmMwZ1g0UExvelo1cVJQalFwbUxZVjNLYkIwRFRGZW1KYWlUZ0UKcERXMXdhNURnUTNDVzFsSWR1TlAvZm1DR2ZrZ1FUUXc2ak9GL1hiUmdNWkVFZzJPclZJNXRZRm9wd0tCZ0VSOQppcWpFdGg1VkdlakNqWStMaVpUdmNVdnNLVWs0dGM2c3R1ZXFtaUU2ZFc3UGhzT3F1cDFmOW9aZWoxaTVDbTFMCm45dThMSlJmKzFHV3pnZDNIT3NxeVhsYjdHbkRlVi9BNkhCSzg4YjJLb05uL01rNG1ETGdZWDEvckh2U3JVOUEKRUNSR2x2WTZFVFpBeFhQWFFzR3hWS25uYXRHdGlGUjVBS05senMwUEFvR0FhNStYK0RVcUdoOWFFNUlEM3dydgpqa2p4UTJLTEZKQ05TcThmOUdTdXZwdmdYc3RIaDZ3S29NNnZNd0lTaGpnWHVVUkg4VWI0dWhSc1dueE1pbGRGCjdFRStRYVdVOWpuQ20ySFFZQXJmWHJBV3c2REJ1ZGlTa0JxZ0tjNkhqREh1bjVmWGxZVW84VWVzTk1RT3JnN2IKYnlkUVo1LzRWLzFvU1dQRVRrN2pTcjA9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURDVENDQWZHZ0F3SUJBZ0lVRlNudEVuK1R2NkhNMnhKUmVFQ0pwSmNDN2lVd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZERVNNQkFHQTFVRUF3d0piRzlqWVd4b2IzTjBNQjRYRFRJME1ERXdPREU1TlRReE5Gb1hEVE0wTURFdwpOVEU1TlRReE5Gb3dGREVTTUJBR0ExVUVBd3dKYkc5allXeG9iM04wTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGCkFBT0NBUThBTUlJQkNnS0NBUUVBNDYzUksvVDlFMmxCRW5iS1JPbk1xTnRqVlBNb1VhMjJKVmZWUjlyMXI1ejcKRTRWM01ZWjNmbkZLblRSQWJNSWNJQlpWRUhJUDNrbGNjSzYvdDU0Zm14VTJWajNHUHRTMEdNWDZTaHJLd2wwSAp3Zk5pd3lTR1ExK0lCMksxbjkyZHU5bzF6ZFYzaFFDcllzQXlvQTExN2p4KzYrWlIrN3d0MFYzZ0gzNmk4RUNZCldYL2phQnpiYi9UQi9TSFZKRUg1Z3hXYlNQd2tNd3EyK3Zyemt1aEpsQS8wTmhUUndjNE1Rb0xocW9EY2dYRUcKOFNmVUo0UG1ZeUJib1JtRnlaYk1rVkxYc2NzMDBlZjc5RlRTdkZMR1lFUVJZVjZxd2IvMGFBZ2h2c1ZTV0xabgpkRUxLN3pjdzZodWtKNDFBZHRjMVY2c3loSkZ0b0ViWTEzTU9MMkVIb1FJREFRQUJvMU13VVRBZEJnTlZIUTRFCkZnUVVmcnkvS0R0YW13TWxSUXNGUGJCaHpkdjJVNWN3SHdZRFZSMGpCQmd3Rm9BVWZyeS9LRHRhbXdNbFJRc0YKUGJCaHpkdjJVNWN3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBeVlzdApWdmV3S1JScHVZUldjNFhHNlduWXBoVWR5WkxNb0lscTBzeVoxYWo2WWJxb0s5Tk1IQVlFbkN2U292NnpJWk9hCnRyaHVVY2Y5R0Z6NWUwaUoyeklsRGMzMTJJd3N2NDF4aUMvYnMxNmtFbjhZZi9TdWpFWGFzajd2bUEzSHJGV2YKd1pUSC95Rkw1YXpvL2YrbEExUTI4WXdxRnBIbWxlMHkwTzUzVXRoNHAwdG13bG51K0NyTzlmSHAza1RsYjdmRAo2bXFmazlOcnQ4dE9DNGFIWURvcXRZVWdaaHg1OHhzSE1PVGV0S2VSbHA4SE1GOW9ST3RyaXo0blltNkloVHdvCjVrMUExM1MzQmpheGtaQ3lQWENnWHNzdVhhZ05MYXNycjVRcStWZ2RiL25EaFZlaFY4K1o0SjBZbnp5OU1ac0UKSDFOMU5mTXRzQStQRXF0UFhBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
210- certPEM , err := base64 .StdEncoding .DecodeString (encodedCertData )
211- g .Expect (err ).NotTo (HaveOccurred ())
210+ testCertPath := "../../test/setup/certificate"
212211
213212 tests := []struct {
214213 name string
215214 cluster * infrav1.AzureCluster
216215 secret * corev1.Secret
217216 identity * infrav1.AzureClusterIdentity
218217 ActiveDirectoryAuthorityHost string
218+ cacheExpect func (* mock_azure.MockCredentialCache )
219219 }{
220220 {
221221 name : "workload identity" ,
@@ -235,6 +235,14 @@ func TestGetTokenCredential(t *testing.T) {
235235 TenantID : fakeTenantID ,
236236 },
237237 },
238+ cacheExpect : func (cache * mock_azure.MockCredentialCache ) {
239+ cache .EXPECT ().GetOrStoreWorkloadIdentity (gomock .Cond (func (opts * azidentity.WorkloadIdentityCredentialOptions ) bool {
240+ // ignore tracing provider
241+ return opts .TenantID == fakeTenantID &&
242+ opts .ClientID == fakeClientID &&
243+ opts .TokenFilePath == GetProjectedTokenPath ()
244+ }))
245+ },
238246 },
239247 {
240248 name : "manual service principal" ,
@@ -251,6 +259,7 @@ func TestGetTokenCredential(t *testing.T) {
251259 Spec : infrav1.AzureClusterIdentitySpec {
252260 Type : infrav1 .ManualServicePrincipal ,
253261 TenantID : fakeTenantID ,
262+ ClientID : fakeClientID ,
254263 ClientSecret : corev1.SecretReference {
255264 Name : "test-identity-secret" ,
256265 },
@@ -265,6 +274,20 @@ func TestGetTokenCredential(t *testing.T) {
265274 },
266275 },
267276 ActiveDirectoryAuthorityHost : "https://login.microsoftonline.com" ,
277+ cacheExpect : func (cache * mock_azure.MockCredentialCache ) {
278+ cache .EXPECT ().GetOrStoreClientSecret (fakeTenantID , fakeClientID , "fooSecret" , gomock .Cond (func (opts * azidentity.ClientSecretCredentialOptions ) bool {
279+ // ignore tracing provider
280+ return reflect .DeepEqual (opts .ClientOptions .Cloud , cloud.Configuration {
281+ ActiveDirectoryAuthorityHost : "https://login.microsoftonline.com" ,
282+ Services : map [cloud.ServiceName ]cloud.ServiceConfiguration {
283+ cloud .ResourceManager : {
284+ Audience : "" ,
285+ Endpoint : "" ,
286+ },
287+ },
288+ })
289+ }))
290+ },
268291 },
269292 {
270293 name : "service principal" ,
@@ -281,6 +304,7 @@ func TestGetTokenCredential(t *testing.T) {
281304 Spec : infrav1.AzureClusterIdentitySpec {
282305 Type : infrav1 .ServicePrincipal ,
283306 TenantID : fakeTenantID ,
307+ ClientID : fakeClientID ,
284308 ClientSecret : corev1.SecretReference {
285309 Name : "test-identity-secret" ,
286310 },
@@ -295,6 +319,20 @@ func TestGetTokenCredential(t *testing.T) {
295319 },
296320 },
297321 ActiveDirectoryAuthorityHost : "https://login.microsoftonline.com" ,
322+ cacheExpect : func (cache * mock_azure.MockCredentialCache ) {
323+ cache .EXPECT ().GetOrStoreClientSecret (fakeTenantID , fakeClientID , "fooSecret" , gomock .Cond (func (opts * azidentity.ClientSecretCredentialOptions ) bool {
324+ // ignore tracing provider
325+ return reflect .DeepEqual (opts .ClientOptions .Cloud , cloud.Configuration {
326+ ActiveDirectoryAuthorityHost : "https://login.microsoftonline.com" ,
327+ Services : map [cloud.ServiceName ]cloud.ServiceConfiguration {
328+ cloud .ResourceManager : {
329+ Audience : "" ,
330+ Endpoint : "" ,
331+ },
332+ },
333+ })
334+ }))
335+ },
298336 },
299337 {
300338 name : "service principal certificate" ,
@@ -311,17 +349,23 @@ func TestGetTokenCredential(t *testing.T) {
311349 Spec : infrav1.AzureClusterIdentitySpec {
312350 Type : infrav1 .ServicePrincipalCertificate ,
313351 TenantID : fakeTenantID ,
314- CertPath : "../../test/setup/certificate" ,
352+ ClientID : fakeClientID ,
353+ ClientSecret : corev1.SecretReference {
354+ Name : "test-identity-secret" ,
355+ },
315356 },
316357 },
317358 secret : & corev1.Secret {
318359 ObjectMeta : metav1.ObjectMeta {
319360 Name : "test-identity-secret" ,
320361 },
321362 Data : map [string ][]byte {
322- "clientSecret" : certPEM ,
363+ "clientSecret" : [] byte ( "fooSecret" ) ,
323364 },
324365 },
366+ cacheExpect : func (cache * mock_azure.MockCredentialCache ) {
367+ cache .EXPECT ().GetOrStoreClientCert (fakeTenantID , fakeClientID , []byte ("fooSecret" ), gomock .Nil (), gomock .Any ())
368+ },
325369 },
326370 {
327371 name : "service principal certificate with certificate filepath" ,
@@ -338,9 +382,17 @@ func TestGetTokenCredential(t *testing.T) {
338382 Spec : infrav1.AzureClusterIdentitySpec {
339383 Type : infrav1 .ServicePrincipalCertificate ,
340384 TenantID : fakeTenantID ,
341- CertPath : "../../test/setup/certificate" ,
385+ ClientID : fakeClientID ,
386+ CertPath : testCertPath ,
342387 },
343388 },
389+ cacheExpect : func (cache * mock_azure.MockCredentialCache ) {
390+ expectedCert , err := os .ReadFile (testCertPath )
391+ if err != nil {
392+ panic (err )
393+ }
394+ cache .EXPECT ().GetOrStoreClientCert (fakeTenantID , fakeClientID , expectedCert , gomock .Nil (), gomock .Any ())
395+ },
344396 },
345397 {
346398 name : "user-assigned identity" ,
@@ -357,8 +409,15 @@ func TestGetTokenCredential(t *testing.T) {
357409 Spec : infrav1.AzureClusterIdentitySpec {
358410 Type : infrav1 .UserAssignedMSI ,
359411 TenantID : fakeTenantID ,
412+ ClientID : fakeClientID ,
360413 },
361414 },
415+ cacheExpect : func (cache * mock_azure.MockCredentialCache ) {
416+ cache .EXPECT ().GetOrStoreManagedIdentity (gomock .Cond (func (opts * azidentity.ManagedIdentityCredentialOptions ) bool {
417+ // ignore tracing provider
418+ return opts .ID == azidentity .ClientID (fakeClientID )
419+ }))
420+ },
362421 },
363422 }
364423
@@ -377,11 +436,15 @@ func TestGetTokenCredential(t *testing.T) {
377436 initObjects = append (initObjects , tt .secret )
378437 }
379438 fakeClient := fake .NewClientBuilder ().WithScheme (scheme ).WithRuntimeObjects (initObjects ... ).Build ()
380- provider , err := NewAzureCredentialsProvider (context .Background (), fakeClient , tt .cluster .Spec .IdentityRef , "" )
439+
440+ mockCtrl := gomock .NewController (t )
441+ cache := mock_azure .NewMockCredentialCache (mockCtrl )
442+ tt .cacheExpect (cache )
443+
444+ provider , err := NewAzureCredentialsProvider (context .Background (), cache , fakeClient , tt .cluster .Spec .IdentityRef , "" )
381445 g .Expect (err ).NotTo (HaveOccurred ())
382- cred , err : = provider .GetTokenCredential (context .Background (), "" , tt .ActiveDirectoryAuthorityHost , "" )
446+ _ , err = provider .GetTokenCredential (context .Background (), "" , tt .ActiveDirectoryAuthorityHost , "" )
383447 g .Expect (err ).NotTo (HaveOccurred ())
384- g .Expect (cred ).NotTo (BeNil ())
385448 })
386449 }
387450}
0 commit comments