sigs.k8s.io/cluster-api-provider-azure@v1.14.3/exp/controllers/helpers_test.go (about) 1 /* 2 Copyright 2019 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/go-logr/logr" 24 . "github.com/onsi/gomega" 25 "go.uber.org/mock/gomock" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/runtime" 28 "k8s.io/apimachinery/pkg/types" 29 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 30 infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1beta1" 31 gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" 32 "sigs.k8s.io/cluster-api-provider-azure/internal/test/mock_log" 33 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 34 "sigs.k8s.io/controller-runtime/pkg/client" 35 "sigs.k8s.io/controller-runtime/pkg/client/fake" 36 "sigs.k8s.io/controller-runtime/pkg/reconcile" 37 ) 38 39 var ( 40 clusterName = "my-cluster" 41 ) 42 43 func TestAzureClusterToAzureMachinePoolsMapper(t *testing.T) { 44 g := NewWithT(t) 45 scheme := newScheme(g) 46 initObjects := []runtime.Object{ 47 newCluster(clusterName), 48 // Create two Machines with an infrastructure ref and one without. 49 newMachinePoolWithInfrastructureRef(clusterName, "my-machine-0"), 50 newMachinePoolWithInfrastructureRef(clusterName, "my-machine-1"), 51 newMachinePool(clusterName, "my-machine-2"), 52 } 53 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 54 55 sink := mock_log.NewMockLogSink(gomock.NewController(t)) 56 sink.EXPECT().Init(logr.RuntimeInfo{CallDepth: 1}) 57 sink.EXPECT().Enabled(4).Return(true) 58 sink.EXPECT().WithValues("AzureCluster", "my-cluster", "Namespace", "default").Return(sink) 59 sink.EXPECT().Info(4, "gk does not match", "gk", gomock.Any(), "infraGK", gomock.Any()) 60 mapper, err := AzureClusterToAzureMachinePoolsMapper(context.Background(), fakeClient, scheme, logr.New(sink)) 61 g.Expect(err).NotTo(HaveOccurred()) 62 63 requests := mapper(context.Background(), &infrav1.AzureCluster{ 64 ObjectMeta: metav1.ObjectMeta{ 65 Name: clusterName, 66 Namespace: "default", 67 OwnerReferences: []metav1.OwnerReference{ 68 { 69 Name: clusterName, 70 Kind: "Cluster", 71 APIVersion: clusterv1.GroupVersion.String(), 72 }, 73 }, 74 }, 75 }) 76 g.Expect(requests).To(HaveLen(2)) 77 } 78 79 func Test_MachinePoolToInfrastructureMapFunc(t *testing.T) { 80 cases := []struct { 81 Name string 82 Setup func(logMock *mock_log.MockLogSink) 83 MapObjectFactory func(*GomegaWithT) client.Object 84 Expect func(*GomegaWithT, []reconcile.Request) 85 }{ 86 { 87 Name: "MachinePoolToAzureMachinePool", 88 MapObjectFactory: func(g *GomegaWithT) client.Object { 89 return newMachinePoolWithInfrastructureRef("azureCluster", "machinePool") 90 }, 91 Setup: func(logMock *mock_log.MockLogSink) { 92 logMock.EXPECT().Init(logr.RuntimeInfo{CallDepth: 1}) 93 }, 94 Expect: func(g *GomegaWithT, reqs []reconcile.Request) { 95 g.Expect(reqs).To(HaveLen(1)) 96 g.Expect(reqs[0]).To(Equal(reconcile.Request{ 97 NamespacedName: types.NamespacedName{ 98 Name: "azuremachinePool", 99 Namespace: "default", 100 }, 101 })) 102 }, 103 }, 104 { 105 Name: "MachinePoolWithoutMatchingInfraRef", 106 MapObjectFactory: func(g *GomegaWithT) client.Object { 107 return newMachinePool("azureCluster", "machinePool") 108 }, 109 Setup: func(logMock *mock_log.MockLogSink) { 110 ampGK := infrav1exp.GroupVersion.WithKind(infrav1.AzureMachinePoolKind).GroupKind() 111 logMock.EXPECT().Init(logr.RuntimeInfo{CallDepth: 1}) 112 logMock.EXPECT().Enabled(4).Return(true) 113 logMock.EXPECT().Info(4, "gk does not match", "gk", ampGK, "infraGK", gomock.Any()) 114 }, 115 Expect: func(g *GomegaWithT, reqs []reconcile.Request) { 116 g.Expect(reqs).To(BeEmpty()) 117 }, 118 }, 119 { 120 Name: "NotAMachinePool", 121 MapObjectFactory: func(g *GomegaWithT) client.Object { 122 return newCluster("azureCluster") 123 }, 124 Setup: func(logMock *mock_log.MockLogSink) { 125 logMock.EXPECT().Init(logr.RuntimeInfo{CallDepth: 1}) 126 logMock.EXPECT().Enabled(4).Return(true) 127 logMock.EXPECT().Info(4, "attempt to map incorrect type", "type", "*v1beta1.Cluster") 128 }, 129 Expect: func(g *GomegaWithT, reqs []reconcile.Request) { 130 g.Expect(reqs).To(BeEmpty()) 131 }, 132 }, 133 } 134 135 for _, c := range cases { 136 c := c 137 t.Run(c.Name, func(t *testing.T) { 138 g := NewWithT(t) 139 140 mockCtrl := gomock.NewController(t) 141 defer mockCtrl.Finish() 142 143 sink := mock_log.NewMockLogSink(mockCtrl) 144 if c.Setup != nil { 145 c.Setup(sink) 146 } 147 f := MachinePoolToInfrastructureMapFunc(infrav1exp.GroupVersion.WithKind(infrav1.AzureMachinePoolKind), logr.New(sink)) 148 reqs := f(context.TODO(), c.MapObjectFactory(g)) 149 c.Expect(g, reqs) 150 }) 151 } 152 } 153 154 func Test_azureClusterToAzureMachinePoolsFunc(t *testing.T) { 155 cases := []struct { 156 Name string 157 Setup func(*testing.T, *GomegaWithT) (*mock_log.MockLogSink, *gomock.Controller, client.Client) 158 MapObjectFactory func(*GomegaWithT) client.Object 159 Expect func(*GomegaWithT, []reconcile.Request) 160 }{ 161 { 162 Name: "NotAnAzureCluster", 163 MapObjectFactory: func(g *GomegaWithT) client.Object { 164 return newMachinePool("fakeCluster", "bar") 165 }, 166 Setup: func(t *testing.T, g *GomegaWithT) (*mock_log.MockLogSink, *gomock.Controller, client.Client) { 167 t.Helper() 168 mockCtrl := gomock.NewController(t) 169 sink := mock_log.NewMockLogSink(mockCtrl) 170 fakeClient := fake.NewClientBuilder().WithScheme(newScheme(g)).Build() 171 sink.EXPECT().Init(logr.RuntimeInfo{CallDepth: 1}) 172 sink.EXPECT().Error(gomockinternal.ErrStrEq("expected a AzureCluster but got a *v1beta1.MachinePool"), "failed to get AzureCluster") 173 174 return sink, mockCtrl, fakeClient 175 }, 176 Expect: func(g *GomegaWithT, reqs []reconcile.Request) { 177 g.Expect(reqs).To(BeEmpty()) 178 }, 179 }, 180 { 181 Name: "AzureClusterDoesNotExist", 182 MapObjectFactory: func(g *GomegaWithT) client.Object { 183 return newAzureCluster("foo") 184 }, 185 Setup: func(t *testing.T, g *GomegaWithT) (*mock_log.MockLogSink, *gomock.Controller, client.Client) { 186 t.Helper() 187 mockCtrl := gomock.NewController(t) 188 sink := mock_log.NewMockLogSink(mockCtrl) 189 fakeClient := fake.NewClientBuilder().WithScheme(newScheme(g)).Build() 190 sink.EXPECT().Init(logr.RuntimeInfo{CallDepth: 1}) 191 sink.EXPECT().Enabled(4).Return(true) 192 sink.EXPECT().WithValues("AzureCluster", "azurefoo", "Namespace", "default").Return(sink) 193 sink.EXPECT().Info(4, "owning cluster not found") 194 return sink, mockCtrl, fakeClient 195 }, 196 Expect: func(g *GomegaWithT, reqs []reconcile.Request) { 197 g.Expect(reqs).To(BeEmpty()) 198 }, 199 }, 200 { 201 Name: "AzureClusterExistsButDoesNotHaveMachinePools", 202 MapObjectFactory: func(g *GomegaWithT) client.Object { 203 return newAzureCluster("foo") 204 }, 205 Setup: func(t *testing.T, g *GomegaWithT) (*mock_log.MockLogSink, *gomock.Controller, client.Client) { 206 t.Helper() 207 mockCtrl := gomock.NewController(t) 208 sink := mock_log.NewMockLogSink(mockCtrl) 209 logWithValues := mock_log.NewMockLogSink(mockCtrl) 210 const clusterName = "foo" 211 initObj := []runtime.Object{ 212 newCluster(clusterName), 213 newAzureCluster(clusterName), 214 } 215 fakeClient := fake.NewClientBuilder().WithScheme(newScheme(g)).WithRuntimeObjects(initObj...).Build() 216 sink.EXPECT().Init(logr.RuntimeInfo{CallDepth: 1}) 217 sink.EXPECT().WithValues("AzureCluster", "azurefoo", "Namespace", "default").Return(logWithValues) 218 return sink, mockCtrl, fakeClient 219 }, 220 Expect: func(g *GomegaWithT, reqs []reconcile.Request) { 221 g.Expect(reqs).To(BeEmpty()) 222 }, 223 }, 224 { 225 Name: "AzureClusterExistsWithMachinePoolsButNoInfraRefs", 226 MapObjectFactory: func(g *GomegaWithT) client.Object { 227 return newAzureCluster("foo") 228 }, 229 Setup: func(t *testing.T, g *GomegaWithT) (*mock_log.MockLogSink, *gomock.Controller, client.Client) { 230 t.Helper() 231 mockCtrl := gomock.NewController(t) 232 sink := mock_log.NewMockLogSink(mockCtrl) 233 logWithValues := mock_log.NewMockLogSink(mockCtrl) 234 const clusterName = "foo" 235 initObj := []runtime.Object{ 236 newCluster(clusterName), 237 newAzureCluster(clusterName), 238 newMachinePool(clusterName, "pool1"), 239 newMachinePool(clusterName, "pool2"), 240 } 241 fakeClient := fake.NewClientBuilder().WithScheme(newScheme(g)).WithRuntimeObjects(initObj...).Build() 242 sink.EXPECT().Init(logr.RuntimeInfo{CallDepth: 1}) 243 sink.EXPECT().WithValues("AzureCluster", "azurefoo", "Namespace", "default").Return(logWithValues) 244 return sink, mockCtrl, fakeClient 245 }, 246 Expect: func(g *GomegaWithT, reqs []reconcile.Request) { 247 g.Expect(reqs).To(BeEmpty()) 248 }, 249 }, 250 { 251 Name: "AzureClusterExistsWithMachinePoolsWithOneInfraRefs", 252 MapObjectFactory: func(g *GomegaWithT) client.Object { 253 return newAzureCluster("foo") 254 }, 255 Setup: func(t *testing.T, g *GomegaWithT) (*mock_log.MockLogSink, *gomock.Controller, client.Client) { 256 t.Helper() 257 mockCtrl := gomock.NewController(t) 258 sink := mock_log.NewMockLogSink(mockCtrl) 259 logWithValues := mock_log.NewMockLogSink(mockCtrl) 260 const clusterName = "foo" 261 initObj := []runtime.Object{ 262 newCluster(clusterName), 263 newAzureCluster(clusterName), 264 newMachinePool(clusterName, "pool1"), 265 newAzureMachinePool(clusterName, "azurepool2"), 266 newMachinePoolWithInfrastructureRef(clusterName, "pool2"), 267 } 268 fakeClient := fake.NewClientBuilder().WithScheme(newScheme(g)).WithRuntimeObjects(initObj...).Build() 269 sink.EXPECT().Init(logr.RuntimeInfo{CallDepth: 1}) 270 sink.EXPECT().WithValues("AzureCluster", "azurefoo", "Namespace", "default").Return(logWithValues) 271 return sink, mockCtrl, fakeClient 272 }, 273 Expect: func(g *GomegaWithT, reqs []reconcile.Request) { 274 g.Expect(reqs).To(HaveLen(1)) 275 g.Expect(reqs[0]).To(Equal(reconcile.Request{ 276 NamespacedName: types.NamespacedName{ 277 Name: "azurepool2", 278 Namespace: "default", 279 }, 280 })) 281 }, 282 }, 283 } 284 285 for _, c := range cases { 286 c := c 287 t.Run(c.Name, func(t *testing.T) { 288 t.Parallel() 289 g := NewGomegaWithT(t) 290 sink, mockctrl, fakeClient := c.Setup(t, g) 291 defer mockctrl.Finish() 292 293 f := AzureClusterToAzureMachinePoolsFunc(context.Background(), fakeClient, logr.New(sink)) 294 reqs := f(context.TODO(), c.MapObjectFactory(g)) 295 c.Expect(g, reqs) 296 }) 297 } 298 }