sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/loadbalancers/loadbalancers_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 loadbalancers 18 19 import ( 20 "context" 21 "io" 22 "net/http" 23 "strings" 24 "testing" 25 26 "github.com/Azure/azure-sdk-for-go/sdk/azcore" 27 . "github.com/onsi/gomega" 28 "go.uber.org/mock/gomock" 29 "k8s.io/utils/ptr" 30 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 31 "sigs.k8s.io/cluster-api-provider-azure/azure" 32 "sigs.k8s.io/cluster-api-provider-azure/azure/services/async/mock_async" 33 "sigs.k8s.io/cluster-api-provider-azure/azure/services/loadbalancers/mock_loadbalancers" 34 gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" 35 "sigs.k8s.io/cluster-api-provider-azure/util/reconciler" 36 ) 37 38 var ( 39 fakePublicAPILBSpec = LBSpec{ 40 Name: "my-publiclb", 41 ResourceGroup: "my-rg", 42 SubscriptionID: "123", 43 ClusterName: "my-cluster", 44 Location: "my-location", 45 Role: infrav1.APIServerRole, 46 Type: infrav1.Public, 47 SKU: infrav1.SKUStandard, 48 SubnetName: "my-cp-subnet", 49 BackendPoolName: "my-publiclb-backendPool", 50 IdleTimeoutInMinutes: ptr.To[int32](4), 51 FrontendIPConfigs: []infrav1.FrontendIP{ 52 { 53 Name: "my-publiclb-frontEnd", 54 PublicIP: &infrav1.PublicIPSpec{ 55 Name: "my-publicip", 56 DNSName: "my-cluster.12345.mydomain.com", 57 }, 58 }, 59 }, 60 APIServerPort: 6443, 61 } 62 63 fakeInternalAPILBSpec = LBSpec{ 64 Name: "my-private-lb", 65 ResourceGroup: "my-rg", 66 SubscriptionID: "123", 67 ClusterName: "my-cluster", 68 Location: "my-location", 69 Role: infrav1.APIServerRole, 70 Type: infrav1.Internal, 71 SKU: infrav1.SKUStandard, 72 SubnetName: "my-cp-subnet", 73 BackendPoolName: "my-private-lb-backendPool", 74 IdleTimeoutInMinutes: ptr.To[int32](4), 75 FrontendIPConfigs: []infrav1.FrontendIP{ 76 { 77 Name: "my-private-lb-frontEnd", 78 FrontendIPClass: infrav1.FrontendIPClass{ 79 PrivateIPAddress: "10.0.0.10", 80 }, 81 }, 82 }, 83 APIServerPort: 6443, 84 } 85 86 fakeNodeOutboundLBSpec = LBSpec{ 87 Name: "my-cluster", 88 ResourceGroup: "my-rg", 89 SubscriptionID: "123", 90 ClusterName: "my-cluster", 91 Location: "my-location", 92 Role: infrav1.NodeOutboundRole, 93 Type: infrav1.Public, 94 SKU: infrav1.SKUStandard, 95 BackendPoolName: "my-cluster-outboundBackendPool", 96 IdleTimeoutInMinutes: ptr.To[int32](30), 97 FrontendIPConfigs: []infrav1.FrontendIP{ 98 { 99 Name: "my-cluster-frontEnd", 100 PublicIP: &infrav1.PublicIPSpec{ 101 Name: "outbound-publicip", 102 }, 103 }, 104 }, 105 } 106 107 internalError = &azcore.ResponseError{ 108 RawResponse: &http.Response{ 109 Body: io.NopCloser(strings.NewReader("#: Internal Server Error: StatusCode=500")), 110 StatusCode: http.StatusInternalServerError, 111 }, 112 } 113 ) 114 115 func TestReconcileLoadBalancer(t *testing.T) { 116 testcases := []struct { 117 name string 118 expectedError string 119 expect func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) 120 }{ 121 { 122 name: "noop if no LBSpecs are found", 123 expectedError: "", 124 expect: func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { 125 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 126 s.LBSpecs().Return([]azure.ResourceSpecGetter{}) 127 }, 128 }, 129 { 130 name: "fail to create a public LB", 131 expectedError: "#: Internal Server Error: StatusCode=500", 132 expect: func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { 133 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 134 s.LBSpecs().Return([]azure.ResourceSpecGetter{&fakePublicAPILBSpec}) 135 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicAPILBSpec, serviceName).Return(nil, internalError) 136 s.UpdatePutStatus(infrav1.LoadBalancersReadyCondition, serviceName, internalError) 137 }, 138 }, 139 { 140 name: "create public apiserver LB", 141 expectedError: "", 142 expect: func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { 143 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 144 s.LBSpecs().Return([]azure.ResourceSpecGetter{&fakePublicAPILBSpec}) 145 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicAPILBSpec, serviceName).Return(nil, nil) 146 s.UpdatePutStatus(infrav1.LoadBalancersReadyCondition, serviceName, nil) 147 }, 148 }, 149 { 150 name: "create internal apiserver LB", 151 expectedError: "", 152 expect: func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { 153 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 154 s.LBSpecs().Return([]azure.ResourceSpecGetter{&fakeInternalAPILBSpec}) 155 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakeInternalAPILBSpec, serviceName).Return(nil, nil) 156 s.UpdatePutStatus(infrav1.LoadBalancersReadyCondition, serviceName, nil) 157 }, 158 }, 159 { 160 name: "create node outbound LB", 161 expectedError: "", 162 expect: func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { 163 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 164 s.LBSpecs().Return([]azure.ResourceSpecGetter{&fakeNodeOutboundLBSpec}) 165 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakeNodeOutboundLBSpec, serviceName).Return(nil, nil) 166 s.UpdatePutStatus(infrav1.LoadBalancersReadyCondition, serviceName, nil) 167 }, 168 }, 169 { 170 name: "create multiple LBs", 171 expectedError: "", 172 expect: func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { 173 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 174 s.LBSpecs().Return([]azure.ResourceSpecGetter{&fakePublicAPILBSpec, &fakeInternalAPILBSpec, &fakeNodeOutboundLBSpec}) 175 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicAPILBSpec, serviceName).Return(nil, nil) 176 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakeInternalAPILBSpec, serviceName).Return(nil, nil) 177 r.CreateOrUpdateResource(gomockinternal.AContext(), &fakeNodeOutboundLBSpec, serviceName).Return(nil, nil) 178 s.UpdatePutStatus(infrav1.LoadBalancersReadyCondition, serviceName, nil) 179 }, 180 }, 181 } 182 183 for _, tc := range testcases { 184 tc := tc 185 t.Run(tc.name, func(t *testing.T) { 186 g := NewWithT(t) 187 t.Parallel() 188 189 mockCtrl := gomock.NewController(t) 190 defer mockCtrl.Finish() 191 192 scopeMock := mock_loadbalancers.NewMockLBScope(mockCtrl) 193 asyncMock := mock_async.NewMockReconciler(mockCtrl) 194 195 tc.expect(scopeMock.EXPECT(), asyncMock.EXPECT()) 196 197 s := &Service{ 198 Scope: scopeMock, 199 Reconciler: asyncMock, 200 } 201 err := s.Reconcile(context.TODO()) 202 if tc.expectedError != "" { 203 g.Expect(err).To(HaveOccurred()) 204 g.Expect(err.Error()).To(ContainSubstring(tc.expectedError)) 205 } else { 206 g.Expect(err).NotTo(HaveOccurred()) 207 } 208 }) 209 } 210 } 211 212 func TestDeleteLoadBalancer(t *testing.T) { 213 testcases := []struct { 214 name string 215 expectedError string 216 expect func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) 217 }{ 218 { 219 name: "noop if no LBSpecs are found", 220 expectedError: "", 221 expect: func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { 222 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 223 s.LBSpecs().Return([]azure.ResourceSpecGetter{}) 224 }, 225 }, 226 { 227 name: "delete a load balancer", 228 expectedError: "", 229 expect: func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { 230 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 231 s.LBSpecs().Return([]azure.ResourceSpecGetter{&fakePublicAPILBSpec}) 232 r.DeleteResource(gomockinternal.AContext(), &fakePublicAPILBSpec, serviceName).Return(nil) 233 s.UpdateDeleteStatus(infrav1.LoadBalancersReadyCondition, serviceName, nil) 234 }, 235 }, 236 { 237 name: "delete multiple load balancers", 238 expectedError: "", 239 expect: func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { 240 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 241 s.LBSpecs().Return([]azure.ResourceSpecGetter{&fakePublicAPILBSpec, &fakeInternalAPILBSpec, &fakeNodeOutboundLBSpec}) 242 r.DeleteResource(gomockinternal.AContext(), &fakePublicAPILBSpec, serviceName).Return(nil) 243 r.DeleteResource(gomockinternal.AContext(), &fakeInternalAPILBSpec, serviceName).Return(nil) 244 r.DeleteResource(gomockinternal.AContext(), &fakeNodeOutboundLBSpec, serviceName).Return(nil) 245 s.UpdateDeleteStatus(infrav1.LoadBalancersReadyCondition, serviceName, nil) 246 }, 247 }, 248 { 249 name: "load balancer deletion fails", 250 expectedError: "#: Internal Server Error: StatusCode=500", 251 expect: func(s *mock_loadbalancers.MockLBScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { 252 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 253 s.LBSpecs().Return([]azure.ResourceSpecGetter{&fakePublicAPILBSpec}) 254 r.DeleteResource(gomockinternal.AContext(), &fakePublicAPILBSpec, serviceName).Return(internalError) 255 s.UpdateDeleteStatus(infrav1.LoadBalancersReadyCondition, serviceName, internalError) 256 }, 257 }, 258 } 259 260 for _, tc := range testcases { 261 tc := tc 262 t.Run(tc.name, func(t *testing.T) { 263 g := NewWithT(t) 264 t.Parallel() 265 266 mockCtrl := gomock.NewController(t) 267 defer mockCtrl.Finish() 268 269 scopeMock := mock_loadbalancers.NewMockLBScope(mockCtrl) 270 asyncMock := mock_async.NewMockReconciler(mockCtrl) 271 272 tc.expect(scopeMock.EXPECT(), asyncMock.EXPECT()) 273 274 s := &Service{ 275 Scope: scopeMock, 276 Reconciler: asyncMock, 277 } 278 279 err := s.Delete(context.TODO()) 280 if tc.expectedError != "" { 281 g.Expect(err).To(HaveOccurred()) 282 g.Expect(err.Error()).To(ContainSubstring(tc.expectedError)) 283 } else { 284 g.Expect(err).NotTo(HaveOccurred()) 285 } 286 }) 287 } 288 }