sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/publicips/publicips_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 publicips
    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/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
    28  	. "github.com/onsi/gomega"
    29  	"go.uber.org/mock/gomock"
    30  	"k8s.io/client-go/kubernetes/scheme"
    31  	"k8s.io/utils/ptr"
    32  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    33  	"sigs.k8s.io/cluster-api-provider-azure/azure"
    34  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/async/mock_async"
    35  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/publicips/mock_publicips"
    36  	gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock"
    37  	"sigs.k8s.io/cluster-api-provider-azure/util/reconciler"
    38  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    39  )
    40  
    41  func init() {
    42  	_ = clusterv1.AddToScheme(scheme.Scheme)
    43  }
    44  
    45  var (
    46  	fakePublicIPSpec1 = PublicIPSpec{
    47  		Name:           "my-publicip",
    48  		ResourceGroup:  "my-rg",
    49  		DNSName:        "fakedns.mydomain.io",
    50  		IsIPv6:         false,
    51  		ClusterName:    "my-cluster",
    52  		Location:       "centralIndia",
    53  		FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
    54  		AdditionalTags: infrav1.Tags{
    55  			"Name": "my-publicip-ipv6",
    56  			"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
    57  		},
    58  	}
    59  	fakePublicIPSpec2 = PublicIPSpec{
    60  		Name:           "my-publicip-2",
    61  		ResourceGroup:  "my-rg",
    62  		DNSName:        "fakedns2-52959.uksouth.cloudapp.azure.com",
    63  		IsIPv6:         false,
    64  		ClusterName:    "my-cluster",
    65  		Location:       "centralIndia",
    66  		FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
    67  		AdditionalTags: infrav1.Tags{
    68  			"Name": "my-publicip-ipv6",
    69  			"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
    70  		},
    71  	}
    72  	fakePublicIPSpec3 = PublicIPSpec{
    73  		Name:           "my-publicip-3",
    74  		ResourceGroup:  "my-rg",
    75  		DNSName:        "",
    76  		IsIPv6:         false,
    77  		ClusterName:    "my-cluster",
    78  		Location:       "centralIndia",
    79  		FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
    80  		AdditionalTags: infrav1.Tags{
    81  			"Name": "my-publicip-ipv6",
    82  			"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
    83  		},
    84  	}
    85  	fakePublicIPSpecIpv6 = PublicIPSpec{
    86  		Name:           "my-publicip-ipv6",
    87  		ResourceGroup:  "my-rg",
    88  		DNSName:        "fakename.mydomain.io",
    89  		IsIPv6:         true,
    90  		ClusterName:    "my-cluster",
    91  		Location:       "centralIndia",
    92  		FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
    93  		AdditionalTags: infrav1.Tags{
    94  			"Name": "my-publicip-ipv6",
    95  			"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
    96  			"foo": "bar",
    97  		},
    98  	}
    99  
   100  	managedTags = armresources.TagsResource{
   101  		Properties: &armresources.Tags{
   102  			Tags: map[string]*string{
   103  				"foo": ptr.To("bar"),
   104  				"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": ptr.To("owned"),
   105  			},
   106  		},
   107  	}
   108  
   109  	unmanagedTags = armresources.TagsResource{
   110  		Properties: &armresources.Tags{
   111  			Tags: map[string]*string{
   112  				"foo":       ptr.To("bar"),
   113  				"something": ptr.To("else"),
   114  			},
   115  		},
   116  	}
   117  
   118  	internalError = &azcore.ResponseError{
   119  		RawResponse: &http.Response{
   120  			Body:       io.NopCloser(strings.NewReader("#: Internal Server Error: StatusCode=500")),
   121  			StatusCode: http.StatusInternalServerError,
   122  		},
   123  	}
   124  )
   125  
   126  func TestReconcilePublicIP(t *testing.T) {
   127  	testcases := []struct {
   128  		name          string
   129  		expectedError string
   130  		expect        func(s *mock_publicips.MockPublicIPScopeMockRecorder, m *mock_async.MockTagsGetterMockRecorder, r *mock_async.MockReconcilerMockRecorder)
   131  	}{
   132  		{
   133  			name:          "noop if no public IPs",
   134  			expectedError: "",
   135  			expect: func(s *mock_publicips.MockPublicIPScopeMockRecorder, m *mock_async.MockTagsGetterMockRecorder, r *mock_async.MockReconcilerMockRecorder) {
   136  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   137  				s.PublicIPSpecs().Return([]azure.ResourceSpecGetter{})
   138  			},
   139  		},
   140  		{
   141  			name:          "successfully create public IPs",
   142  			expectedError: "",
   143  			expect: func(s *mock_publicips.MockPublicIPScopeMockRecorder, m *mock_async.MockTagsGetterMockRecorder, r *mock_async.MockReconcilerMockRecorder) {
   144  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   145  				s.PublicIPSpecs().Return([]azure.ResourceSpecGetter{&fakePublicIPSpec1, &fakePublicIPSpec2, &fakePublicIPSpec3, &fakePublicIPSpecIpv6})
   146  				r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicIPSpec1, serviceName).Return(nil, nil)
   147  				r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicIPSpec2, serviceName).Return(nil, nil)
   148  				r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicIPSpec3, serviceName).Return(nil, nil)
   149  				r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicIPSpecIpv6, serviceName).Return(nil, nil)
   150  				s.UpdatePutStatus(infrav1.PublicIPsReadyCondition, serviceName, nil)
   151  			},
   152  		},
   153  		{
   154  			name:          "fail to create a public IP",
   155  			expectedError: internalError.Error(),
   156  			expect: func(s *mock_publicips.MockPublicIPScopeMockRecorder, m *mock_async.MockTagsGetterMockRecorder, r *mock_async.MockReconcilerMockRecorder) {
   157  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   158  				s.PublicIPSpecs().Return([]azure.ResourceSpecGetter{&fakePublicIPSpec1, &fakePublicIPSpec2, &fakePublicIPSpec3, &fakePublicIPSpecIpv6})
   159  				r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicIPSpec1, serviceName).Return(nil, nil)
   160  				r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicIPSpec2, serviceName).Return(nil, nil)
   161  				r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicIPSpec3, serviceName).Return(nil, internalError)
   162  				r.CreateOrUpdateResource(gomockinternal.AContext(), &fakePublicIPSpecIpv6, serviceName).Return(nil, nil)
   163  				s.UpdatePutStatus(infrav1.PublicIPsReadyCondition, serviceName, internalError)
   164  			},
   165  		},
   166  	}
   167  
   168  	for _, tc := range testcases {
   169  		tc := tc
   170  		t.Run(tc.name, func(t *testing.T) {
   171  			g := NewWithT(t)
   172  
   173  			t.Parallel()
   174  			mockCtrl := gomock.NewController(t)
   175  			defer mockCtrl.Finish()
   176  
   177  			scopeMock := mock_publicips.NewMockPublicIPScope(mockCtrl)
   178  			tagsGetterMock := mock_async.NewMockTagsGetter(mockCtrl)
   179  			reconcilerMock := mock_async.NewMockReconciler(mockCtrl)
   180  
   181  			tc.expect(scopeMock.EXPECT(), tagsGetterMock.EXPECT(), reconcilerMock.EXPECT())
   182  
   183  			s := &Service{
   184  				Scope:      scopeMock,
   185  				TagsGetter: tagsGetterMock,
   186  				Reconciler: reconcilerMock,
   187  			}
   188  
   189  			err := s.Reconcile(context.TODO())
   190  			if tc.expectedError != "" {
   191  				g.Expect(err).To(HaveOccurred())
   192  				g.Expect(err).To(MatchError(tc.expectedError))
   193  			} else {
   194  				g.Expect(err).NotTo(HaveOccurred())
   195  			}
   196  		})
   197  	}
   198  }
   199  
   200  func TestDeletePublicIP(t *testing.T) {
   201  	testcases := []struct {
   202  		name          string
   203  		expectedError string
   204  		expect        func(s *mock_publicips.MockPublicIPScopeMockRecorder, m *mock_async.MockTagsGetterMockRecorder, r *mock_async.MockReconcilerMockRecorder)
   205  	}{
   206  		{
   207  			name:          "noop if no public IPs",
   208  			expectedError: "",
   209  			expect: func(s *mock_publicips.MockPublicIPScopeMockRecorder, m *mock_async.MockTagsGetterMockRecorder, r *mock_async.MockReconcilerMockRecorder) {
   210  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   211  				s.PublicIPSpecs().Return([]azure.ResourceSpecGetter{})
   212  			},
   213  		},
   214  		{
   215  			name:          "successfully delete managed public IPs and ignore unmanaged public IPs",
   216  			expectedError: "",
   217  			expect: func(s *mock_publicips.MockPublicIPScopeMockRecorder, m *mock_async.MockTagsGetterMockRecorder, r *mock_async.MockReconcilerMockRecorder) {
   218  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   219  				s.PublicIPSpecs().Return([]azure.ResourceSpecGetter{&fakePublicIPSpec1, &fakePublicIPSpec2, &fakePublicIPSpec3, &fakePublicIPSpecIpv6})
   220  
   221  				s.SubscriptionID().Return("123")
   222  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpec1.ResourceGroupName(), fakePublicIPSpec1.ResourceName())).Return(managedTags, nil)
   223  				s.ClusterName().Return("my-cluster")
   224  				r.DeleteResource(gomockinternal.AContext(), &fakePublicIPSpec1, serviceName).Return(nil)
   225  
   226  				s.SubscriptionID().Return("123")
   227  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpec2.ResourceGroupName(), fakePublicIPSpec2.ResourceName())).Return(managedTags, nil)
   228  				s.ClusterName().Return("my-cluster")
   229  				r.DeleteResource(gomockinternal.AContext(), &fakePublicIPSpec2, serviceName).Return(nil)
   230  
   231  				s.SubscriptionID().Return("123")
   232  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpec3.ResourceGroupName(), fakePublicIPSpec3.ResourceName())).Return(unmanagedTags, nil)
   233  				s.ClusterName().Return("my-cluster")
   234  
   235  				s.SubscriptionID().Return("123")
   236  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpecIpv6.ResourceGroupName(), fakePublicIPSpecIpv6.ResourceName())).Return(managedTags, nil)
   237  				s.ClusterName().Return("my-cluster")
   238  				r.DeleteResource(gomockinternal.AContext(), &fakePublicIPSpecIpv6, serviceName).Return(nil)
   239  
   240  				s.UpdateDeleteStatus(infrav1.PublicIPsReadyCondition, serviceName, nil)
   241  			},
   242  		},
   243  		{
   244  			name:          "noop if no managed public IPs",
   245  			expectedError: "",
   246  			expect: func(s *mock_publicips.MockPublicIPScopeMockRecorder, m *mock_async.MockTagsGetterMockRecorder, r *mock_async.MockReconcilerMockRecorder) {
   247  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   248  				s.PublicIPSpecs().Return([]azure.ResourceSpecGetter{&fakePublicIPSpec1, &fakePublicIPSpec2, &fakePublicIPSpec3, &fakePublicIPSpecIpv6})
   249  
   250  				s.SubscriptionID().Return("123")
   251  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpec1.ResourceGroupName(), fakePublicIPSpec1.ResourceName())).Return(unmanagedTags, nil)
   252  				s.ClusterName().Return("my-cluster")
   253  
   254  				s.SubscriptionID().Return("123")
   255  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpec2.ResourceGroupName(), fakePublicIPSpec2.ResourceName())).Return(unmanagedTags, nil)
   256  				s.ClusterName().Return("my-cluster")
   257  
   258  				s.SubscriptionID().Return("123")
   259  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpec3.ResourceGroupName(), fakePublicIPSpec3.ResourceName())).Return(unmanagedTags, nil)
   260  				s.ClusterName().Return("my-cluster")
   261  
   262  				s.SubscriptionID().Return("123")
   263  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpecIpv6.ResourceGroupName(), fakePublicIPSpecIpv6.ResourceName())).Return(unmanagedTags, nil)
   264  				s.ClusterName().Return("my-cluster")
   265  			},
   266  		},
   267  		{
   268  			name:          "fail to delete managed public IP",
   269  			expectedError: internalError.Error(),
   270  			expect: func(s *mock_publicips.MockPublicIPScopeMockRecorder, m *mock_async.MockTagsGetterMockRecorder, r *mock_async.MockReconcilerMockRecorder) {
   271  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   272  				s.PublicIPSpecs().Return([]azure.ResourceSpecGetter{&fakePublicIPSpec1, &fakePublicIPSpec2, &fakePublicIPSpec3, &fakePublicIPSpecIpv6})
   273  
   274  				s.SubscriptionID().Return("123")
   275  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpec1.ResourceGroupName(), fakePublicIPSpec1.ResourceName())).Return(managedTags, nil)
   276  				s.ClusterName().Return("my-cluster")
   277  				r.DeleteResource(gomockinternal.AContext(), &fakePublicIPSpec1, serviceName).Return(nil)
   278  
   279  				s.SubscriptionID().Return("123")
   280  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpec2.ResourceGroupName(), fakePublicIPSpec2.ResourceName())).Return(managedTags, nil)
   281  				s.ClusterName().Return("my-cluster")
   282  				r.DeleteResource(gomockinternal.AContext(), &fakePublicIPSpec2, serviceName).Return(nil)
   283  
   284  				s.SubscriptionID().Return("123")
   285  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpec3.ResourceGroupName(), fakePublicIPSpec3.ResourceName())).Return(managedTags, nil)
   286  				s.ClusterName().Return("my-cluster")
   287  				r.DeleteResource(gomockinternal.AContext(), &fakePublicIPSpec3, serviceName).Return(internalError)
   288  
   289  				s.SubscriptionID().Return("123")
   290  				m.GetAtScope(gomockinternal.AContext(), azure.PublicIPID("123", fakePublicIPSpecIpv6.ResourceGroupName(), fakePublicIPSpecIpv6.ResourceName())).Return(managedTags, nil)
   291  				s.ClusterName().Return("my-cluster")
   292  				r.DeleteResource(gomockinternal.AContext(), &fakePublicIPSpecIpv6, serviceName).Return(nil)
   293  
   294  				s.UpdateDeleteStatus(infrav1.PublicIPsReadyCondition, serviceName, internalError)
   295  			},
   296  		},
   297  	}
   298  
   299  	for _, tc := range testcases {
   300  		tc := tc
   301  		t.Run(tc.name, func(t *testing.T) {
   302  			g := NewWithT(t)
   303  
   304  			t.Parallel()
   305  			mockCtrl := gomock.NewController(t)
   306  			defer mockCtrl.Finish()
   307  
   308  			scopeMock := mock_publicips.NewMockPublicIPScope(mockCtrl)
   309  			tagsGetterMock := mock_async.NewMockTagsGetter(mockCtrl)
   310  			reconcilerMock := mock_async.NewMockReconciler(mockCtrl)
   311  
   312  			tc.expect(scopeMock.EXPECT(), tagsGetterMock.EXPECT(), reconcilerMock.EXPECT())
   313  
   314  			s := &Service{
   315  				Scope:      scopeMock,
   316  				TagsGetter: tagsGetterMock,
   317  				Reconciler: reconcilerMock,
   318  			}
   319  
   320  			err := s.Delete(context.TODO())
   321  			if tc.expectedError != "" {
   322  				g.Expect(err).To(HaveOccurred())
   323  				g.Expect(err).To(MatchError(tc.expectedError))
   324  			} else {
   325  				g.Expect(err).NotTo(HaveOccurred())
   326  			}
   327  		})
   328  	}
   329  }