From 412b669b6d2b32bb066a346fac4a1d8c1e4ad849 Mon Sep 17 00:00:00 2001 From: v-xuxin Date: Tue, 12 May 2020 14:40:43 +0000 Subject: [PATCH] Improve unit test coverage for azure publicipclient --- .../azure/clients/publicipclient/BUILD | 2 + .../azure_publicipclient_test.go | 619 ++++++++++++++++++ 2 files changed, 621 insertions(+) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/BUILD b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/BUILD index f6b744b475f..3e9d9702a99 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/BUILD +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/BUILD @@ -29,9 +29,11 @@ go_test( srcs = ["azure_publicipclient_test.go"], embed = [":go_default_library"], deps = [ + "//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library", "//staging/src/k8s.io/legacy-cloud-providers/azure/clients:go_default_library", "//staging/src/k8s.io/legacy-cloud-providers/azure/clients/armclient:go_default_library", "//staging/src/k8s.io/legacy-cloud-providers/azure/clients/armclient/mockarmclient:go_default_library", + "//staging/src/k8s.io/legacy-cloud-providers/azure/retry:go_default_library", "//vendor/github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network:go_default_library", "//vendor/github.com/Azure/go-autorest/autorest:go_default_library", "//vendor/github.com/Azure/go-autorest/autorest/to:go_default_library", diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient_test.go index 9bc8d7b671c..ae712ce5b9b 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient_test.go @@ -26,6 +26,7 @@ import ( "io/ioutil" "net/http" "testing" + "time" "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/Azure/go-autorest/autorest" @@ -33,11 +34,90 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + "k8s.io/client-go/util/flowcontrol" azclients "k8s.io/legacy-cloud-providers/azure/clients" "k8s.io/legacy-cloud-providers/azure/clients/armclient" "k8s.io/legacy-cloud-providers/azure/clients/armclient/mockarmclient" + "k8s.io/legacy-cloud-providers/azure/retry" ) +func TestNew(t *testing.T) { + config := &azclients.ClientConfig{ + SubscriptionID: "sub", + ResourceManagerEndpoint: "endpoint", + Location: "eastus", + RateLimitConfig: &azclients.RateLimitConfig{ + CloudProviderRateLimit: true, + CloudProviderRateLimitQPS: 0.5, + CloudProviderRateLimitBucket: 1, + CloudProviderRateLimitQPSWrite: 0.5, + CloudProviderRateLimitBucketWrite: 1, + }, + Backoff: &retry.Backoff{Steps: 1}, + } + + pipClient := New(config) + assert.Equal(t, "sub", pipClient.subscriptionID) + assert.NotEmpty(t, pipClient.rateLimiterReader) + assert.NotEmpty(t, pipClient.rateLimiterWriter) +} + +func TestGet(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/pip1" + response := &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + } + + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return(response, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + expected := network.PublicIPAddress{Response: autorest.Response{Response: response}} + pipClient := getTestPublicIPAddressClient(armClient) + result, rerr := pipClient.Get(context.TODO(), "rg", "pip1", "") + assert.Equal(t, expected, result) + assert.Nil(t, rerr) +} + +func TestGetNeverRateLimiter(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + pipGetErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider rate limited(%s) for operation %q", "read", "PublicIPGet"), + Retriable: true, + } + + armClient := mockarmclient.NewMockInterface(ctrl) + pipClient := getTestPublicIPAddressClientWithNeverRateLimiter(armClient) + expected := network.PublicIPAddress{} + result, rerr := pipClient.Get(context.TODO(), "rg", "pip1", "") + assert.Equal(t, expected, result) + assert.Equal(t, pipGetErr, rerr) +} + +func TestGetRetryAfterReader(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + pipGetErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider throttled for operation %s with reason %q", "PublicIPGet", "client throttled"), + Retriable: true, + RetryAfter: getFutureTime(), + } + + armClient := mockarmclient.NewMockInterface(ctrl) + pipClient := getTestPublicIPAddressClientWithRetryAfterReader(armClient) + expected := network.PublicIPAddress{} + result, rerr := pipClient.Get(context.TODO(), "rg", "pip1", "") + assert.Equal(t, expected, result) + assert.Equal(t, pipGetErr, rerr) +} + func TestGetNotFound(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -80,6 +160,167 @@ func TestGetInternalError(t *testing.T) { assert.Equal(t, http.StatusInternalServerError, rerr.HTTPStatusCode) } +func TestGetThrottle(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/pip1" + response := &http.Response{ + StatusCode: http.StatusTooManyRequests, + Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + } + throttleErr := &retry.Error{ + HTTPStatusCode: http.StatusTooManyRequests, + RawError: fmt.Errorf("error"), + Retriable: true, + RetryAfter: time.Unix(100, 0), + } + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return(response, throttleErr).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + pipClient := getTestPublicIPAddressClient(armClient) + result, rerr := pipClient.Get(context.TODO(), "rg", "pip1", "") + assert.Empty(t, result) + assert.Equal(t, throttleErr, rerr) +} + +func TestGetVMSSPublicIPAddress(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/vmss/virtualMachines/0/networkInterfaces/nic/ipconfigurations/ipConfig/publicipaddresses/pip1" + testPip := network.PublicIPAddress{ + PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{ + IPAddress: to.StringPtr("10.10.10.10"), + }, + } + pip, err := testPip.MarshalJSON() + assert.Nil(t, err) + response := &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pip)), + } + + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().GetResourceWithDecorators(gomock.Any(), resourceID, gomock.Any()).Return(response, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + pipClient := getTestPublicIPAddressClient(armClient) + expected := network.PublicIPAddress{ + Response: autorest.Response{Response: response}, + PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{ + IPAddress: to.StringPtr("10.10.10.10"), + }, + } + result, rerr := pipClient.GetVirtualMachineScaleSetPublicIPAddress(context.TODO(), "rg", "vmss", "0", "nic", "ipConfig", "pip1", "") + assert.Equal(t, expected, result) + assert.Nil(t, rerr) +} + +func TestGetVMSSPublicIPAddressNeverRateLimiter(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + pipGetErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider rate limited(%s) for operation %q", "read", "VMSSPublicIPGet"), + Retriable: true, + } + + armClient := mockarmclient.NewMockInterface(ctrl) + pipClient := getTestPublicIPAddressClientWithNeverRateLimiter(armClient) + expected := network.PublicIPAddress{} + result, rerr := pipClient.GetVirtualMachineScaleSetPublicIPAddress(context.TODO(), "rg", "vmss", "0", "nic", "ipConfig", "pip1", "") + assert.Equal(t, expected, result) + assert.Equal(t, pipGetErr, rerr) +} + +func TestGetVMSSPublicIPAddressRetryAfterReader(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + pipGetErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider throttled for operation %s with reason %q", "VMSSPublicIPGet", "client throttled"), + Retriable: true, + RetryAfter: getFutureTime(), + } + + armClient := mockarmclient.NewMockInterface(ctrl) + pipClient := getTestPublicIPAddressClientWithRetryAfterReader(armClient) + expected := network.PublicIPAddress{} + result, rerr := pipClient.GetVirtualMachineScaleSetPublicIPAddress(context.TODO(), "rg", "vmss", "0", "nic", "ipConfig", "pip1", "") + assert.Equal(t, expected, result) + assert.Equal(t, pipGetErr, rerr) +} + +func TestGetVMSSPublicIPAddressNotFound(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/vmss/virtualMachines/0/networkInterfaces/nic/ipconfigurations/ipConfig/publicipaddresses/pip1" + response := &http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + } + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().GetResourceWithDecorators(gomock.Any(), resourceID, gomock.Any()).Return(response, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + pipClient := getTestPublicIPAddressClient(armClient) + expected := network.PublicIPAddress{Response: autorest.Response{}} + result, rerr := pipClient.GetVirtualMachineScaleSetPublicIPAddress(context.TODO(), "rg", "vmss", "0", "nic", "ipConfig", "pip1", "") + assert.Equal(t, expected, result) + assert.NotNil(t, rerr) + assert.Equal(t, http.StatusNotFound, rerr.HTTPStatusCode) +} + +func TestGetVMSSPublicIPAddressInternalError(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/vmss/virtualMachines/0/networkInterfaces/nic/ipconfigurations/ipConfig/publicipaddresses/pip1" + response := &http.Response{ + StatusCode: http.StatusInternalServerError, + Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + } + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().GetResourceWithDecorators(gomock.Any(), resourceID, gomock.Any()).Return(response, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + pipClient := getTestPublicIPAddressClient(armClient) + expected := network.PublicIPAddress{Response: autorest.Response{}} + result, rerr := pipClient.GetVirtualMachineScaleSetPublicIPAddress(context.TODO(), "rg", "vmss", "0", "nic", "ipConfig", "pip1", "") + + assert.Equal(t, expected, result) + assert.NotNil(t, rerr) + assert.Equal(t, http.StatusInternalServerError, rerr.HTTPStatusCode) +} + +func TestGetVMSSPublicIPAddressThrottle(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/vmss/virtualMachines/0/networkInterfaces/nic/ipconfigurations/ipConfig/publicipaddresses/pip1" + response := &http.Response{ + StatusCode: http.StatusTooManyRequests, + Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + } + throttleErr := &retry.Error{ + HTTPStatusCode: http.StatusTooManyRequests, + RawError: fmt.Errorf("error"), + Retriable: true, + RetryAfter: time.Unix(100, 0), + } + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().GetResourceWithDecorators(gomock.Any(), resourceID, gomock.Any()).Return(response, throttleErr).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + pipClient := getTestPublicIPAddressClient(armClient) + result, rerr := pipClient.GetVirtualMachineScaleSetPublicIPAddress(context.TODO(), "rg", "vmss", "0", "nic", "ipConfig", "pip1", "") + assert.Empty(t, result) + assert.Equal(t, throttleErr, rerr) +} + func TestList(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -102,6 +343,223 @@ func TestList(t *testing.T) { assert.Equal(t, 3, len(result)) } +func TestListNotFound(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses" + response := &http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + } + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return(response, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + pipClient := getTestPublicIPAddressClient(armClient) + expected := []network.PublicIPAddress{} + result, rerr := pipClient.List(context.TODO(), "rg") + assert.Equal(t, expected, result) + assert.NotNil(t, rerr) + assert.Equal(t, http.StatusNotFound, rerr.HTTPStatusCode) +} + +func TestListInternalError(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses" + response := &http.Response{ + StatusCode: http.StatusInternalServerError, + Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + } + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return(response, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + pipClient := getTestPublicIPAddressClient(armClient) + expected := []network.PublicIPAddress{} + result, rerr := pipClient.List(context.TODO(), "rg") + assert.Equal(t, expected, result) + assert.NotNil(t, rerr) + assert.Equal(t, http.StatusInternalServerError, rerr.HTTPStatusCode) +} + +func TestListThrottle(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses" + armClient := mockarmclient.NewMockInterface(ctrl) + response := &http.Response{ + StatusCode: http.StatusTooManyRequests, + Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + } + throttleErr := &retry.Error{ + HTTPStatusCode: http.StatusTooManyRequests, + RawError: fmt.Errorf("error"), + Retriable: true, + RetryAfter: time.Unix(100, 0), + } + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return(response, throttleErr).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + pipClient := getTestPublicIPAddressClient(armClient) + result, rerr := pipClient.List(context.TODO(), "rg") + assert.Empty(t, result) + assert.NotNil(t, rerr) + assert.Equal(t, throttleErr, rerr) +} + +func TestListWithListResponderError(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses" + armClient := mockarmclient.NewMockInterface(ctrl) + pipList := []network.PublicIPAddress{getTestPublicIPAddress("pip1"), getTestPublicIPAddress("pip2"), getTestPublicIPAddress("pip3")} + responseBody, err := json.Marshal(network.PublicIPAddressListResult{Value: &pipList}) + assert.Nil(t, err) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return( + &http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(bytes.NewReader(responseBody)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + pipClient := getTestPublicIPAddressClient(armClient) + result, rerr := pipClient.List(context.TODO(), "rg") + assert.NotNil(t, rerr) + assert.Equal(t, 0, len(result)) +} + +func TestListNeverRateLimiter(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + pipListErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider rate limited(%s) for operation %q", "read", "PublicIPList"), + Retriable: true, + } + + armClient := mockarmclient.NewMockInterface(ctrl) + pipClient := getTestPublicIPAddressClientWithNeverRateLimiter(armClient) + result, rerr := pipClient.List(context.TODO(), "rg") + assert.Equal(t, 0, len(result)) + assert.NotNil(t, rerr) + assert.Equal(t, pipListErr, rerr) +} + +func TestListRetryAfterReader(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + pipListErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider throttled for operation %s with reason %q", "PublicIPList", "client throttled"), + Retriable: true, + RetryAfter: getFutureTime(), + } + + armClient := mockarmclient.NewMockInterface(ctrl) + pipClient := getTestPublicIPAddressClientWithRetryAfterReader(armClient) + result, rerr := pipClient.List(context.TODO(), "rg") + assert.Equal(t, 0, len(result)) + assert.NotNil(t, rerr) + assert.Equal(t, pipListErr, rerr) +} + +func TestListNextResultsMultiPages(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + tests := []struct { + prepareErr error + sendErr *retry.Error + }{ + { + prepareErr: nil, + sendErr: nil, + }, + { + prepareErr: fmt.Errorf("error"), + }, + { + sendErr: &retry.Error{RawError: fmt.Errorf("error")}, + }, + } + + lastResult := network.PublicIPAddressListResult{ + NextLink: to.StringPtr("next"), + } + + for _, test := range tests { + armClient := mockarmclient.NewMockInterface(ctrl) + req := &http.Request{ + Method: "GET", + } + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(req, test.prepareErr) + if test.prepareErr == nil { + armClient.EXPECT().Send(gomock.Any(), req).Return(&http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"foo":"bar"}`))), + }, test.sendErr) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()) + } + + pipClient := getTestPublicIPAddressClient(armClient) + result, err := pipClient.listNextResults(context.TODO(), lastResult) + if test.prepareErr != nil || test.sendErr != nil { + assert.NotNil(t, err) + } else { + assert.NoError(t, err) + } + if test.prepareErr != nil { + assert.Empty(t, result) + } else { + assert.NotEmpty(t, result) + } + } +} + +func TestListNextResultsMultiPagesWithListResponderError(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + test := struct { + prepareErr error + sendErr *retry.Error + }{ + prepareErr: nil, + sendErr: nil, + } + + lastResult := network.PublicIPAddressListResult{ + NextLink: to.StringPtr("next"), + } + + armClient := mockarmclient.NewMockInterface(ctrl) + req := &http.Request{ + Method: "GET", + } + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(req, test.prepareErr) + if test.prepareErr == nil { + armClient.EXPECT().Send(gomock.Any(), req).Return(&http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"foo":"bar"}`))), + }, test.sendErr) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()) + } + + response := &http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(bytes.NewBuffer([]byte(`{"foo":"bar"}`))), + } + expected := network.PublicIPAddressListResult{} + expected.Response = autorest.Response{Response: response} + pipClient := getTestPublicIPAddressClient(armClient) + result, err := pipClient.listNextResults(context.TODO(), lastResult) + assert.NotNil(t, err) + assert.Equal(t, expected, result) +} + func TestCreateOrUpdate(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -120,6 +578,84 @@ func TestCreateOrUpdate(t *testing.T) { assert.Nil(t, rerr) } +func TestCreateOrUpdateWithCreateOrUpdateResponderError(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + pip := getTestPublicIPAddress("pip1") + armClient := mockarmclient.NewMockInterface(ctrl) + response := &http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + } + armClient.EXPECT().PutResource(gomock.Any(), to.String(pip.ID), pip).Return(response, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + pipClient := getTestPublicIPAddressClient(armClient) + rerr := pipClient.CreateOrUpdate(context.TODO(), "rg", "pip1", pip) + assert.NotNil(t, rerr) +} + +func TestCreateOrUpdateNeverRateLimiter(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + pipCreateOrUpdateErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider rate limited(%s) for operation %q", "write", "PublicIPCreateOrUpdate"), + Retriable: true, + } + + armClient := mockarmclient.NewMockInterface(ctrl) + pipClient := getTestPublicIPAddressClientWithNeverRateLimiter(armClient) + pip := getTestPublicIPAddress("pip1") + rerr := pipClient.CreateOrUpdate(context.TODO(), "rg", "pip1", pip) + assert.NotNil(t, rerr) + assert.Equal(t, pipCreateOrUpdateErr, rerr) +} + +func TestCreateOrUpdateRetryAfterReader(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + pipCreateOrUpdateErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider throttled for operation %s with reason %q", "PublicIPCreateOrUpdate", "client throttled"), + Retriable: true, + RetryAfter: getFutureTime(), + } + + pip := getTestPublicIPAddress("pip1") + armClient := mockarmclient.NewMockInterface(ctrl) + pipClient := getTestPublicIPAddressClientWithRetryAfterReader(armClient) + rerr := pipClient.CreateOrUpdate(context.TODO(), "rg", "pip1", pip) + assert.NotNil(t, rerr) + assert.Equal(t, pipCreateOrUpdateErr, rerr) +} + +func TestCreateOrUpdateThrottle(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + response := &http.Response{ + StatusCode: http.StatusTooManyRequests, + Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + } + throttleErr := &retry.Error{ + HTTPStatusCode: http.StatusTooManyRequests, + RawError: fmt.Errorf("error"), + Retriable: true, + RetryAfter: time.Unix(100, 0), + } + + pip := getTestPublicIPAddress("pip1") + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().PutResource(gomock.Any(), to.String(pip.ID), pip).Return(response, throttleErr).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + pipClient := getTestPublicIPAddressClient(armClient) + rerr := pipClient.CreateOrUpdate(context.TODO(), "rg", "pip1", pip) + assert.NotNil(t, rerr) + assert.Equal(t, throttleErr, rerr) +} + func TestDelete(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -133,6 +669,65 @@ func TestDelete(t *testing.T) { assert.Nil(t, rerr) } +func TestDeleteNeverRateLimiter(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + pipDeleteErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider rate limited(%s) for operation %q", "write", "PublicIPDelete"), + Retriable: true, + } + + armClient := mockarmclient.NewMockInterface(ctrl) + pipClient := getTestPublicIPAddressClientWithNeverRateLimiter(armClient) + rerr := pipClient.Delete(context.TODO(), "rg", "pip1") + assert.NotNil(t, rerr) + assert.Equal(t, pipDeleteErr, rerr) +} + +func TestDeleteRetryAfterReader(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + pipDeleteErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider throttled for operation %s with reason %q", "PublicIPDelete", "client throttled"), + Retriable: true, + RetryAfter: getFutureTime(), + } + + armClient := mockarmclient.NewMockInterface(ctrl) + pipClient := getTestPublicIPAddressClientWithRetryAfterReader(armClient) + rerr := pipClient.Delete(context.TODO(), "rg", "pip1") + assert.NotNil(t, rerr) + assert.Equal(t, pipDeleteErr, rerr) +} + +func TestDeleteThrottle(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + throttleErr := &retry.Error{ + HTTPStatusCode: http.StatusTooManyRequests, + RawError: fmt.Errorf("error"), + Retriable: true, + RetryAfter: time.Unix(100, 0), + } + + pip := getTestPublicIPAddress("pip1") + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().DeleteResource(gomock.Any(), to.String(pip.ID), "").Return(throttleErr).Times(1) + + pipClient := getTestPublicIPAddressClient(armClient) + rerr := pipClient.Delete(context.TODO(), "rg", "pip1") + assert.NotNil(t, rerr) + assert.Equal(t, throttleErr, rerr) +} + +// 2065-01-24 05:20:00 +0000 UTC +func getFutureTime() time.Time { + return time.Unix(3000000000, 0) +} + func getTestPublicIPAddress(name string) network.PublicIPAddress { return network.PublicIPAddress{ ID: to.StringPtr(fmt.Sprintf("/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/%s", name)), @@ -150,3 +745,27 @@ func getTestPublicIPAddressClient(armClient armclient.Interface) *Client { rateLimiterWriter: rateLimiterWriter, } } + +func getTestPublicIPAddressClientWithNeverRateLimiter(armClient armclient.Interface) *Client { + rateLimiterReader := flowcontrol.NewFakeNeverRateLimiter() + rateLimiterWriter := flowcontrol.NewFakeNeverRateLimiter() + return &Client{ + armClient: armClient, + subscriptionID: "subscriptionID", + rateLimiterReader: rateLimiterReader, + rateLimiterWriter: rateLimiterWriter, + } +} + +func getTestPublicIPAddressClientWithRetryAfterReader(armClient armclient.Interface) *Client { + rateLimiterReader := flowcontrol.NewFakeAlwaysRateLimiter() + rateLimiterWriter := flowcontrol.NewFakeAlwaysRateLimiter() + return &Client{ + armClient: armClient, + subscriptionID: "subscriptionID", + rateLimiterReader: rateLimiterReader, + rateLimiterWriter: rateLimiterWriter, + RetryAfterReader: getFutureTime(), + RetryAfterWriter: getFutureTime(), + } +}