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  }