sigs.k8s.io/cluster-api-provider-azure@v1.14.3/controllers/azurejson_machinepool_controller_test.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package controllers 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 24 "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" 25 "github.com/google/go-cmp/cmp" 26 . "github.com/onsi/gomega" 27 "go.uber.org/mock/gomock" 28 corev1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/apimachinery/pkg/types" 32 "k8s.io/client-go/tools/record" 33 "k8s.io/utils/ptr" 34 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 35 "sigs.k8s.io/cluster-api-provider-azure/azure" 36 "sigs.k8s.io/cluster-api-provider-azure/azure/services/identities" 37 "sigs.k8s.io/cluster-api-provider-azure/azure/services/identities/mock_identities" 38 infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1beta1" 39 "sigs.k8s.io/cluster-api-provider-azure/util/reconciler" 40 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 41 expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" 42 ctrl "sigs.k8s.io/controller-runtime" 43 "sigs.k8s.io/controller-runtime/pkg/client/fake" 44 ) 45 46 func TestAzureJSONPoolReconciler(t *testing.T) { 47 scheme, err := newScheme() 48 if err != nil { 49 t.Error(err) 50 } 51 52 cluster := &clusterv1.Cluster{ 53 ObjectMeta: metav1.ObjectMeta{ 54 Name: "my-cluster", 55 }, 56 Spec: clusterv1.ClusterSpec{ 57 InfrastructureRef: &corev1.ObjectReference{ 58 APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1", 59 Kind: infrav1.AzureClusterKind, 60 Name: "my-azure-cluster", 61 }, 62 }, 63 } 64 65 azureCluster := &infrav1.AzureCluster{ 66 ObjectMeta: metav1.ObjectMeta{ 67 Name: "my-azure-cluster", 68 OwnerReferences: []metav1.OwnerReference{ 69 { 70 APIVersion: "cluster.x-k8s.io/v1beta1", 71 Kind: "Cluster", 72 Name: "my-cluster", 73 }, 74 }, 75 }, 76 Spec: infrav1.AzureClusterSpec{ 77 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 78 SubscriptionID: "123", 79 IdentityRef: &corev1.ObjectReference{ 80 Name: "fake-identity", 81 Namespace: "default", 82 Kind: "AzureClusterIdentity", 83 }, 84 }, 85 NetworkSpec: infrav1.NetworkSpec{ 86 Subnets: infrav1.Subnets{ 87 { 88 SubnetClassSpec: infrav1.SubnetClassSpec{ 89 Name: "node", 90 Role: infrav1.SubnetNode, 91 }, 92 }, 93 }, 94 }, 95 }, 96 } 97 98 machinePool := &expv1.MachinePool{ 99 ObjectMeta: metav1.ObjectMeta{ 100 Name: "my-machine-pool", 101 Labels: map[string]string{ 102 clusterv1.ClusterNameLabel: "my-cluster", 103 }, 104 OwnerReferences: []metav1.OwnerReference{ 105 { 106 APIVersion: "cluster.x-k8s.io/v1beta1", 107 Kind: "Cluster", 108 Name: "my-cluster", 109 }, 110 }, 111 }, 112 } 113 114 azureMachinePool := &infrav1exp.AzureMachinePool{ 115 ObjectMeta: metav1.ObjectMeta{ 116 Name: "my-azure-machine-pool", 117 OwnerReferences: []metav1.OwnerReference{ 118 { 119 APIVersion: "cluster.x-k8s.io/v1beta1", 120 Kind: "Cluster", 121 Name: "my-cluster", 122 }, 123 { 124 APIVersion: "cluster.x-k8s.io/v1beta1", 125 Kind: "MachinePool", 126 Name: "my-machine-pool", 127 }, 128 }, 129 }, 130 } 131 132 fakeIdentity := &infrav1.AzureClusterIdentity{ 133 ObjectMeta: metav1.ObjectMeta{ 134 Name: "fake-identity", 135 Namespace: "default", 136 }, 137 Spec: infrav1.AzureClusterIdentitySpec{ 138 Type: infrav1.ServicePrincipal, 139 TenantID: "fake-tenantid", 140 }, 141 } 142 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 143 144 cases := map[string]struct { 145 objects []runtime.Object 146 fail bool 147 err string 148 }{ 149 "should reconcile normally": { 150 objects: []runtime.Object{ 151 cluster, 152 azureCluster, 153 machinePool, 154 azureMachinePool, 155 fakeIdentity, 156 fakeSecret, 157 }, 158 }, 159 "missing azure cluster should return error": { 160 objects: []runtime.Object{ 161 cluster, 162 machinePool, 163 azureMachinePool, 164 fakeIdentity, 165 fakeSecret, 166 }, 167 fail: true, 168 err: "failed to create cluster scope for cluster /my-cluster: azureclusters.infrastructure.cluster.x-k8s.io \"my-azure-cluster\" not found", 169 }, 170 "infra ref is nil": { 171 objects: []runtime.Object{ 172 &clusterv1.Cluster{ 173 ObjectMeta: metav1.ObjectMeta{ 174 Name: "my-cluster", 175 }, 176 Spec: clusterv1.ClusterSpec{ 177 InfrastructureRef: nil, 178 }, 179 }, 180 azureCluster, 181 machinePool, 182 azureMachinePool, 183 fakeIdentity, 184 fakeSecret, 185 }, 186 fail: false, 187 }, 188 "infra ref is not an azure cluster": { 189 objects: []runtime.Object{ 190 &clusterv1.Cluster{ 191 ObjectMeta: metav1.ObjectMeta{ 192 Name: "my-cluster", 193 }, 194 Spec: clusterv1.ClusterSpec{ 195 InfrastructureRef: &corev1.ObjectReference{ 196 APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1", 197 Kind: "FooCluster", 198 Name: "my-foo-cluster", 199 }, 200 }, 201 }, 202 azureCluster, 203 machinePool, 204 azureMachinePool, 205 fakeIdentity, 206 fakeSecret, 207 }, 208 fail: true, 209 err: "failed to create cluster scope for cluster /my-cluster: unsupported infrastructure type \"FooCluster\", should be AzureCluster or AzureManagedCluster", 210 }, 211 } 212 213 t.Setenv("AZURE_CLIENT_ID", "fooClient") 214 t.Setenv("AZURE_CLIENT_SECRET", "fooSecret") 215 t.Setenv("AZURE_TENANT_ID", "fooTenant") 216 217 for name, tc := range cases { 218 t.Run(name, func(t *testing.T) { 219 client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tc.objects...).Build() 220 221 reconciler := &AzureJSONMachinePoolReconciler{ 222 Client: client, 223 Recorder: record.NewFakeRecorder(128), 224 } 225 226 _, err := reconciler.Reconcile(context.Background(), ctrl.Request{ 227 NamespacedName: types.NamespacedName{ 228 Namespace: "", 229 Name: "my-azure-machine-pool", 230 }, 231 }) 232 233 if tc.fail { 234 if diff := cmp.Diff(tc.err, err.Error()); diff != "" { 235 t.Error(diff) 236 } 237 } else { 238 if err != nil { 239 t.Errorf("expected success, but got error: %s", err.Error()) 240 } 241 } 242 }) 243 } 244 } 245 246 func TestAzureJSONPoolReconcilerUserAssignedIdentities(t *testing.T) { 247 g := NewWithT(t) 248 ctrlr := gomock.NewController(t) 249 defer ctrlr.Finish() 250 req := ctrl.Request{NamespacedName: types.NamespacedName{Name: "fake-machine-pool", Namespace: "fake-ns"}} 251 ctx := context.Background() 252 scheme, err := newScheme() 253 g.Expect(err).NotTo(HaveOccurred()) 254 255 azureMP := &infrav1exp.AzureMachinePool{ 256 ObjectMeta: metav1.ObjectMeta{ 257 Name: "fake-machine-pool", 258 Namespace: "fake-ns", 259 Labels: map[string]string{ 260 clusterv1.ClusterNameLabel: "fake-cluster", 261 }, 262 OwnerReferences: []metav1.OwnerReference{ 263 { 264 APIVersion: fmt.Sprintf("%s/%s", expv1.GroupVersion.Group, expv1.GroupVersion.Version), 265 Kind: "MachinePool", 266 Name: "fake-other-machine-pool", 267 Controller: to.Ptr(true), 268 }, 269 }, 270 }, 271 Spec: infrav1exp.AzureMachinePoolSpec{ 272 UserAssignedIdentities: []infrav1.UserAssignedIdentity{ 273 { 274 ProviderID: "azure:///subscriptions/123/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/fake-provider-id", 275 }, 276 }, 277 }, 278 } 279 280 cluster := &clusterv1.Cluster{ 281 ObjectMeta: metav1.ObjectMeta{ 282 Name: "fake-cluster", 283 Namespace: "fake-ns", 284 }, 285 Spec: clusterv1.ClusterSpec{ 286 InfrastructureRef: &corev1.ObjectReference{ 287 Kind: "AzureCluster", 288 Name: "fake-azure-cluster", 289 Namespace: "fake-ns", 290 }, 291 }, 292 } 293 294 ownerMP := &expv1.MachinePool{ 295 ObjectMeta: metav1.ObjectMeta{ 296 Name: "fake-other-machine-pool", 297 Namespace: "fake-ns", 298 Labels: map[string]string{ 299 clusterv1.ClusterNameLabel: "fake-cluster", 300 }, 301 }, 302 } 303 304 azureCluster := &infrav1.AzureCluster{ 305 ObjectMeta: metav1.ObjectMeta{ 306 Name: "fake-azure-cluster", 307 Namespace: "fake-ns", 308 OwnerReferences: []metav1.OwnerReference{ 309 { 310 APIVersion: "cluster.x-k8s.io/v1beta1", 311 Kind: "Cluster", 312 Name: "my-cluster", 313 }, 314 }, 315 }, 316 Spec: infrav1.AzureClusterSpec{ 317 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 318 SubscriptionID: "123", 319 IdentityRef: &corev1.ObjectReference{ 320 Name: "fake-identity", 321 Namespace: "default", 322 Kind: "AzureClusterIdentity", 323 }, 324 }, 325 NetworkSpec: infrav1.NetworkSpec{ 326 Subnets: infrav1.Subnets{ 327 { 328 SubnetClassSpec: infrav1.SubnetClassSpec{ 329 Name: "node", 330 Role: infrav1.SubnetNode, 331 }, 332 }, 333 }, 334 }, 335 }, 336 } 337 apiVersion, kind := infrav1.GroupVersion.WithKind("AzureMachinePool").ToAPIVersionAndKind() 338 339 fakeIdentity := &infrav1.AzureClusterIdentity{ 340 ObjectMeta: metav1.ObjectMeta{ 341 Name: "fake-identity", 342 Namespace: "default", 343 }, 344 Spec: infrav1.AzureClusterIdentitySpec{ 345 Type: infrav1.ServicePrincipal, 346 ClientSecret: corev1.SecretReference{ 347 Name: azureMP.Name, 348 Namespace: "fake-ns", 349 }, 350 TenantID: "fake-tenantid", 351 }, 352 } 353 354 sec := &corev1.Secret{ 355 ObjectMeta: metav1.ObjectMeta{ 356 Name: azureMP.Name, 357 Namespace: "fake-ns", 358 Labels: map[string]string{ 359 "fake-cluster": string(infrav1.ResourceLifecycleOwned), 360 }, 361 OwnerReferences: []metav1.OwnerReference{ 362 { 363 APIVersion: apiVersion, 364 Kind: kind, 365 Name: azureMP.GetName(), 366 Controller: ptr.To(true), 367 }, 368 }, 369 }, 370 Data: map[string][]byte{ 371 "clientSecret": []byte("fooSecret"), 372 }, 373 } 374 375 client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(azureMP, ownerMP, cluster, azureCluster, sec, fakeIdentity).Build() 376 rec := AzureJSONMachinePoolReconciler{ 377 Client: client, 378 Recorder: record.NewFakeRecorder(42), 379 Timeouts: reconciler.Timeouts{}, 380 } 381 id := "azure:///subscriptions/123/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/fake-provider-id" 382 getClient = func(auth azure.Authorizer) (identities.Client, error) { 383 mockClient := mock_identities.NewMockClient(ctrlr) 384 mockClient.EXPECT().GetClientID(gomock.Any(), gomock.Any()).Return(id, nil) 385 return mockClient, nil 386 } 387 388 _, err = rec.Reconcile(ctx, req) 389 g.Expect(err).NotTo(HaveOccurred()) 390 }