sigs.k8s.io/cluster-api-provider-azure@v1.14.3/controllers/azuremanagedcontrolplane_controller_test.go (about)

     1  /*
     2  Copyright 2023 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  	"testing"
    22  
    23  	asocontainerservicev1preview "github.com/Azure/azure-service-operator/v2/api/containerservice/v1api20230315preview"
    24  	asocontainerservicev1 "github.com/Azure/azure-service-operator/v2/api/containerservice/v1api20231001"
    25  	asonetworkv1 "github.com/Azure/azure-service-operator/v2/api/network/v1api20201101"
    26  	asoresourcesv1 "github.com/Azure/azure-service-operator/v2/api/resources/v1api20200601"
    27  	. "github.com/onsi/gomega"
    28  	"go.uber.org/mock/gomock"
    29  	corev1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/types"
    33  	"k8s.io/client-go/tools/record"
    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/mock_azure"
    37  	"sigs.k8s.io/cluster-api-provider-azure/azure/scope"
    38  	"sigs.k8s.io/cluster-api-provider-azure/internal/test"
    39  	"sigs.k8s.io/cluster-api-provider-azure/util/reconciler"
    40  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    41  	"sigs.k8s.io/cluster-api/util/patch"
    42  	ctrl "sigs.k8s.io/controller-runtime"
    43  	"sigs.k8s.io/controller-runtime/pkg/client"
    44  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    45  )
    46  
    47  func TestClusterToAzureManagedControlPlane(t *testing.T) {
    48  	tests := []struct {
    49  		name            string
    50  		controlPlaneRef *corev1.ObjectReference
    51  		expected        []ctrl.Request
    52  	}{
    53  		{
    54  			name:            "nil",
    55  			controlPlaneRef: nil,
    56  			expected:        nil,
    57  		},
    58  		{
    59  			name: "bad kind",
    60  			controlPlaneRef: &corev1.ObjectReference{
    61  				Kind: "NotAzureManagedControlPlane",
    62  			},
    63  			expected: nil,
    64  		},
    65  		{
    66  			name: "ok",
    67  			controlPlaneRef: &corev1.ObjectReference{
    68  				Kind:      infrav1.AzureManagedControlPlaneKind,
    69  				Name:      "name",
    70  				Namespace: "namespace",
    71  			},
    72  			expected: []ctrl.Request{
    73  				{
    74  					NamespacedName: types.NamespacedName{
    75  						Name:      "name",
    76  						Namespace: "namespace",
    77  					},
    78  				},
    79  			},
    80  		},
    81  	}
    82  
    83  	for _, test := range tests {
    84  		t.Run(test.name, func(t *testing.T) {
    85  			g := NewWithT(t)
    86  			actual := (&AzureManagedControlPlaneReconciler{}).ClusterToAzureManagedControlPlane(context.TODO(), &clusterv1.Cluster{
    87  				Spec: clusterv1.ClusterSpec{
    88  					ControlPlaneRef: test.controlPlaneRef,
    89  				},
    90  			})
    91  			if test.expected == nil {
    92  				g.Expect(actual).To(BeNil())
    93  			} else {
    94  				g.Expect(actual).To(Equal(test.expected))
    95  			}
    96  		})
    97  	}
    98  }
    99  
   100  func TestAzureManagedControlPlaneReconcilePaused(t *testing.T) {
   101  	g := NewWithT(t)
   102  
   103  	ctx := context.Background()
   104  
   105  	sb := runtime.NewSchemeBuilder(
   106  		clusterv1.AddToScheme,
   107  		infrav1.AddToScheme,
   108  		asoresourcesv1.AddToScheme,
   109  		asocontainerservicev1.AddToScheme,
   110  		asonetworkv1.AddToScheme,
   111  		corev1.AddToScheme,
   112  		asocontainerservicev1preview.AddToScheme,
   113  	)
   114  	s := runtime.NewScheme()
   115  	g.Expect(sb.AddToScheme(s)).To(Succeed())
   116  	c := fake.NewClientBuilder().
   117  		WithScheme(s).
   118  		Build()
   119  
   120  	recorder := record.NewFakeRecorder(1)
   121  
   122  	reconciler := &AzureManagedControlPlaneReconciler{
   123  		Client:                                   c,
   124  		Recorder:                                 recorder,
   125  		Timeouts:                                 reconciler.Timeouts{},
   126  		WatchFilterValue:                         "",
   127  		getNewAzureManagedControlPlaneReconciler: newAzureManagedControlPlaneReconciler,
   128  	}
   129  	name := test.RandomName("paused", 10)
   130  	namespace := "default"
   131  
   132  	cluster := &clusterv1.Cluster{
   133  		ObjectMeta: metav1.ObjectMeta{
   134  			Name:      name,
   135  			Namespace: namespace,
   136  		},
   137  		Spec: clusterv1.ClusterSpec{
   138  			Paused: true,
   139  		},
   140  	}
   141  	g.Expect(c.Create(ctx, cluster)).To(Succeed())
   142  
   143  	fakeIdentity := &infrav1.AzureClusterIdentity{
   144  		ObjectMeta: metav1.ObjectMeta{
   145  			Name:      "fake-identity",
   146  			Namespace: "default",
   147  		},
   148  		Spec: infrav1.AzureClusterIdentitySpec{
   149  			Type: infrav1.ServicePrincipal,
   150  			ClientSecret: corev1.SecretReference{
   151  				Name:      "fooSecret",
   152  				Namespace: "default",
   153  			},
   154  			TenantID: "fake-tenantid",
   155  		},
   156  	}
   157  	fakeSecret := &corev1.Secret{
   158  		ObjectMeta: metav1.ObjectMeta{
   159  			Name:      "fooSecret",
   160  			Namespace: "default",
   161  		},
   162  		Data: map[string][]byte{
   163  			"clientSecret": []byte("fooSecret"),
   164  		},
   165  	}
   166  	g.Expect(c.Create(ctx, fakeIdentity)).To(Succeed())
   167  	g.Expect(c.Create(ctx, fakeSecret)).To(Succeed())
   168  
   169  	instance := &infrav1.AzureManagedControlPlane{
   170  		ObjectMeta: metav1.ObjectMeta{
   171  			Name:      name,
   172  			Namespace: namespace,
   173  			OwnerReferences: []metav1.OwnerReference{
   174  				{
   175  					Kind:       "Cluster",
   176  					APIVersion: clusterv1.GroupVersion.String(),
   177  					Name:       cluster.Name,
   178  				},
   179  			},
   180  		},
   181  		Spec: infrav1.AzureManagedControlPlaneSpec{
   182  			AzureManagedControlPlaneClassSpec: infrav1.AzureManagedControlPlaneClassSpec{
   183  				SubscriptionID: "something",
   184  				VirtualNetwork: infrav1.ManagedControlPlaneVirtualNetwork{
   185  					ManagedControlPlaneVirtualNetworkClassSpec: infrav1.ManagedControlPlaneVirtualNetworkClassSpec{
   186  						Name: name,
   187  						Subnet: infrav1.ManagedControlPlaneSubnet{
   188  							Name: "subnet",
   189  						},
   190  					},
   191  				},
   192  				FleetsMember: &infrav1.FleetsMemberClassSpec{
   193  					Group:                "fleets",
   194  					ManagerName:          "fleets-manager",
   195  					ManagerResourceGroup: "fleets-manager-rg",
   196  				},
   197  				IdentityRef: &corev1.ObjectReference{
   198  					Name:      "fake-identity",
   199  					Namespace: "default",
   200  					Kind:      "AzureClusterIdentity",
   201  				},
   202  			},
   203  			ResourceGroupName: name,
   204  		},
   205  	}
   206  	g.Expect(c.Create(ctx, instance)).To(Succeed())
   207  
   208  	rg := &asoresourcesv1.ResourceGroup{
   209  		ObjectMeta: metav1.ObjectMeta{
   210  			Name:      name,
   211  			Namespace: namespace,
   212  		},
   213  	}
   214  	g.Expect(c.Create(ctx, rg)).To(Succeed())
   215  
   216  	mc := &asocontainerservicev1.ManagedCluster{
   217  		ObjectMeta: metav1.ObjectMeta{
   218  			Name:      name,
   219  			Namespace: namespace,
   220  		},
   221  	}
   222  	g.Expect(c.Create(ctx, mc)).To(Succeed())
   223  
   224  	vnet := &asonetworkv1.VirtualNetwork{
   225  		ObjectMeta: metav1.ObjectMeta{
   226  			Name:      name,
   227  			Namespace: namespace,
   228  		},
   229  	}
   230  	g.Expect(c.Create(ctx, vnet)).To(Succeed())
   231  
   232  	fleetsMember := &asocontainerservicev1preview.FleetsMember{
   233  		ObjectMeta: metav1.ObjectMeta{
   234  			Name:      name,
   235  			Namespace: namespace,
   236  		},
   237  	}
   238  	g.Expect(c.Create(ctx, fleetsMember)).To(Succeed())
   239  
   240  	subnet := &asonetworkv1.VirtualNetworksSubnet{
   241  		ObjectMeta: metav1.ObjectMeta{
   242  			Name:      name + "-subnet",
   243  			Namespace: namespace,
   244  		},
   245  	}
   246  	g.Expect(c.Create(ctx, subnet)).To(Succeed())
   247  
   248  	result, err := reconciler.Reconcile(context.Background(), ctrl.Request{
   249  		NamespacedName: client.ObjectKey{
   250  			Namespace: instance.Namespace,
   251  			Name:      instance.Name,
   252  		},
   253  	})
   254  
   255  	g.Expect(err).NotTo(HaveOccurred())
   256  	g.Expect(result.RequeueAfter).To(BeZero())
   257  }
   258  
   259  func TestAzureManagedControlPlaneReconcileNormal(t *testing.T) {
   260  	g := NewWithT(t)
   261  	ctx := context.Background()
   262  	cp := &infrav1.AzureManagedControlPlane{
   263  		ObjectMeta: metav1.ObjectMeta{
   264  			Name:      "fake-azmp",
   265  			Namespace: "fake-ns",
   266  		},
   267  		Spec: infrav1.AzureManagedControlPlaneSpec{
   268  			AzureManagedControlPlaneClassSpec: infrav1.AzureManagedControlPlaneClassSpec{
   269  				Version: "0.0.1",
   270  			},
   271  		},
   272  		Status: infrav1.AzureManagedControlPlaneStatus{
   273  			Ready:       false,
   274  			Initialized: false,
   275  		},
   276  	}
   277  	scheme, err := newScheme()
   278  	g.Expect(err).NotTo(HaveOccurred())
   279  
   280  	client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(cp).WithStatusSubresource(cp).Build()
   281  	amcpr := &AzureManagedControlPlaneReconciler{
   282  		Client: client,
   283  	}
   284  
   285  	helper, err := patch.NewHelper(cp, client)
   286  	g.Expect(err).NotTo(HaveOccurred())
   287  
   288  	scopes := &scope.ManagedControlPlaneScope{
   289  		Cluster: &clusterv1.Cluster{
   290  			ObjectMeta: metav1.ObjectMeta{
   291  				Name:      "fake-cluster",
   292  				Namespace: "fake-ns",
   293  			},
   294  		},
   295  		Client:       client,
   296  		PatchHelper:  helper,
   297  		ControlPlane: cp,
   298  	}
   299  	scopes.SetAdminKubeconfigData(createFakeKubeConfig())
   300  	scopes.SetUserKubeconfigData(createFakeKubeConfig())
   301  
   302  	amcpr.getNewAzureManagedControlPlaneReconciler = func(scope *scope.ManagedControlPlaneScope) (*azureManagedControlPlaneService, error) {
   303  		ctrlr := gomock.NewController(t)
   304  		svcr := mock_azure.NewMockServiceReconciler(ctrlr)
   305  		svcr.EXPECT().Reconcile(gomock.Any()).Return(nil)
   306  
   307  		return &azureManagedControlPlaneService{
   308  			kubeclient: scope.Client,
   309  			scope:      scope,
   310  			services: []azure.ServiceReconciler{
   311  				svcr,
   312  			},
   313  		}, nil
   314  	}
   315  
   316  	_, err = amcpr.reconcileNormal(ctx, scopes)
   317  	g.Expect(err).To(HaveOccurred())
   318  }
   319  
   320  func createFakeKubeConfig() []byte {
   321  	return []byte(`
   322    apiVersion: v1
   323    kind: Config
   324    clusters:
   325    - cluster:
   326        certificate-authority-data: UEhPTlkK
   327        server: https://1.1.1.1
   328      name: production
   329    contexts:
   330    - context:
   331        cluster: production
   332        user: production
   333      name: production
   334    current-context: production`)
   335  }