sigs.k8s.io/cluster-api-provider-azure@v1.14.3/controllers/azurejson_machine_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  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/apimachinery/pkg/types"
    28  	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
    29  	"k8s.io/client-go/tools/record"
    30  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    31  	infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1beta1"
    32  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    33  	expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
    34  	ctrl "sigs.k8s.io/controller-runtime"
    35  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    36  	"sigs.k8s.io/controller-runtime/pkg/event"
    37  )
    38  
    39  func TestUnclonedMachinesPredicate(t *testing.T) {
    40  	cases := map[string]struct {
    41  		expected    bool
    42  		labels      map[string]string
    43  		annotations map[string]string
    44  	}{
    45  		"uncloned worker node should return true": {
    46  			expected:    true,
    47  			labels:      nil,
    48  			annotations: nil,
    49  		},
    50  		"uncloned control plane node should return true": {
    51  			expected: true,
    52  			labels: map[string]string{
    53  				clusterv1.MachineControlPlaneLabel: "",
    54  			},
    55  			annotations: nil,
    56  		},
    57  		"cloned node should return false": {
    58  			expected: false,
    59  			labels:   nil,
    60  			annotations: map[string]string{
    61  				clusterv1.TemplateClonedFromGroupKindAnnotation: infrav1.GroupVersion.WithKind("AzureMachineTemplate").GroupKind().String(),
    62  			},
    63  		},
    64  	}
    65  
    66  	for name, tc := range cases {
    67  		tc := tc
    68  		t.Run(name, func(t *testing.T) {
    69  			t.Parallel()
    70  			machine := &infrav1.AzureMachine{
    71  				ObjectMeta: metav1.ObjectMeta{
    72  					Labels:      tc.labels,
    73  					Annotations: tc.annotations,
    74  				},
    75  			}
    76  			e := event.GenericEvent{
    77  				Object: machine,
    78  			}
    79  			filter := filterUnclonedMachinesPredicate{}
    80  			if filter.Generic(e) != tc.expected {
    81  				t.Errorf("expected: %t, got %t", tc.expected, filter.Generic(e))
    82  			}
    83  		})
    84  	}
    85  }
    86  
    87  func TestAzureJSONMachineReconciler(t *testing.T) {
    88  	scheme, err := newScheme()
    89  	if err != nil {
    90  		t.Error(err)
    91  	}
    92  
    93  	cluster := &clusterv1.Cluster{
    94  		ObjectMeta: metav1.ObjectMeta{
    95  			Name: "my-cluster",
    96  		},
    97  		Spec: clusterv1.ClusterSpec{
    98  			InfrastructureRef: &corev1.ObjectReference{
    99  				APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
   100  				Kind:       infrav1.AzureClusterKind,
   101  				Name:       "my-azure-cluster",
   102  			},
   103  		},
   104  	}
   105  
   106  	azureCluster := &infrav1.AzureCluster{
   107  		ObjectMeta: metav1.ObjectMeta{
   108  			Name: "my-azure-cluster",
   109  			OwnerReferences: []metav1.OwnerReference{
   110  				{
   111  					APIVersion: "cluster.x-k8s.io/v1beta1",
   112  					Kind:       "Cluster",
   113  					Name:       "my-cluster",
   114  				},
   115  			},
   116  		},
   117  		Spec: infrav1.AzureClusterSpec{
   118  			AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   119  				SubscriptionID: "123",
   120  				IdentityRef: &corev1.ObjectReference{
   121  					Name:      "fake-identity",
   122  					Namespace: "default",
   123  				},
   124  			},
   125  			NetworkSpec: infrav1.NetworkSpec{
   126  				Subnets: infrav1.Subnets{
   127  					{
   128  						SubnetClassSpec: infrav1.SubnetClassSpec{
   129  							Name: "node",
   130  							Role: infrav1.SubnetNode,
   131  						},
   132  					},
   133  				},
   134  			},
   135  		},
   136  	}
   137  
   138  	azureMachine := &infrav1.AzureMachine{
   139  		ObjectMeta: metav1.ObjectMeta{
   140  			Name: "my-machine",
   141  			Labels: map[string]string{
   142  				clusterv1.ClusterNameLabel: "my-cluster",
   143  			},
   144  			OwnerReferences: []metav1.OwnerReference{
   145  				{
   146  					APIVersion: "cluster.x-k8s.io/v1beta1",
   147  					Kind:       "Cluster",
   148  					Name:       "my-cluster",
   149  				},
   150  			},
   151  		},
   152  	}
   153  
   154  	fakeIdentity := &infrav1.AzureClusterIdentity{
   155  		ObjectMeta: metav1.ObjectMeta{
   156  			Name:      "fake-identity",
   157  			Namespace: "default",
   158  		},
   159  		Spec: infrav1.AzureClusterIdentitySpec{
   160  			Type:     infrav1.ServicePrincipal,
   161  			TenantID: "fake-tenantid",
   162  		},
   163  	}
   164  	fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
   165  
   166  	cases := map[string]struct {
   167  		objects []runtime.Object
   168  		fail    bool
   169  		err     string
   170  	}{
   171  		"should reconcile normally": {
   172  			objects: []runtime.Object{
   173  				cluster,
   174  				azureCluster,
   175  				azureMachine,
   176  				fakeIdentity,
   177  				fakeSecret,
   178  			},
   179  		},
   180  		"missing azure cluster should return error": {
   181  			objects: []runtime.Object{
   182  				cluster,
   183  				azureMachine,
   184  			},
   185  			fail: true,
   186  			err:  "azureclusters.infrastructure.cluster.x-k8s.io \"my-azure-cluster\" not found",
   187  		},
   188  		"infra ref is nil": {
   189  			objects: []runtime.Object{
   190  				&clusterv1.Cluster{
   191  					ObjectMeta: metav1.ObjectMeta{
   192  						Name: "my-cluster",
   193  					},
   194  					Spec: clusterv1.ClusterSpec{
   195  						InfrastructureRef: nil,
   196  					},
   197  				},
   198  				azureCluster,
   199  				azureMachine,
   200  				fakeIdentity,
   201  				fakeSecret,
   202  			},
   203  			fail: false,
   204  		},
   205  		"infra ref is not an azure cluster": {
   206  			objects: []runtime.Object{
   207  				&clusterv1.Cluster{
   208  					ObjectMeta: metav1.ObjectMeta{
   209  						Name: "my-cluster",
   210  					},
   211  					Spec: clusterv1.ClusterSpec{
   212  						InfrastructureRef: &corev1.ObjectReference{
   213  							APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
   214  							Kind:       "FooCluster",
   215  							Name:       "my-foo-cluster",
   216  						},
   217  					},
   218  				},
   219  				azureCluster,
   220  				azureMachine,
   221  				fakeIdentity,
   222  				fakeSecret,
   223  			},
   224  			fail: false,
   225  		},
   226  	}
   227  
   228  	for name, tc := range cases {
   229  		t.Run(name, func(t *testing.T) {
   230  			client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tc.objects...).Build()
   231  
   232  			reconciler := &AzureJSONMachineReconciler{
   233  				Client:   client,
   234  				Recorder: record.NewFakeRecorder(128),
   235  			}
   236  
   237  			_, err := reconciler.Reconcile(context.Background(), ctrl.Request{
   238  				NamespacedName: types.NamespacedName{
   239  					Namespace: "",
   240  					Name:      "my-machine",
   241  				},
   242  			})
   243  			if tc.fail {
   244  				if diff := cmp.Diff(tc.err, err.Error()); diff != "" {
   245  					t.Error(diff)
   246  				}
   247  			} else {
   248  				if err != nil {
   249  					t.Errorf("expected success, but got error: %s", err.Error())
   250  				}
   251  			}
   252  		})
   253  	}
   254  }
   255  
   256  func newScheme() (*runtime.Scheme, error) {
   257  	scheme := runtime.NewScheme()
   258  	schemeFn := []func(*runtime.Scheme) error{
   259  		clientgoscheme.AddToScheme,
   260  		infrav1.AddToScheme,
   261  		clusterv1.AddToScheme,
   262  		infrav1exp.AddToScheme,
   263  		expv1.AddToScheme,
   264  		corev1.AddToScheme,
   265  	}
   266  	for _, fn := range schemeFn {
   267  		fn := fn
   268  		if err := fn(scheme); err != nil {
   269  			return nil, err
   270  		}
   271  	}
   272  	return scheme, nil
   273  }