diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/routetableclient/BUILD b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/routetableclient/BUILD index 9086aee0257..110d68459c1 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/routetableclient/BUILD +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/routetableclient/BUILD @@ -28,9 +28,11 @@ go_test( srcs = ["azure_routetableclient_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/routetableclient/azure_routetableclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/routetableclient/azure_routetableclient_test.go index 4440fb249c4..b59d102dc95 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/routetableclient/azure_routetableclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/routetableclient/azure_routetableclient_test.go @@ -25,6 +25,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" @@ -32,11 +33,61 @@ 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" ) +// 2065-01-24 05:20:00 +0000 UTC +func getFutureTime() time.Time { + return time.Unix(3000000000, 0) +} + +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}, + } + + routetableClient := New(config) + assert.Equal(t, "sub", routetableClient.subscriptionID) + assert.NotEmpty(t, routetableClient.rateLimiterReader) + assert.NotEmpty(t, routetableClient.rateLimiterWriter) +} + +func TestGet(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/routeTables/rt1" + 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) + + routetableClient := getTestRouteTableClient(armClient) + expected := network.RouteTable{} + expected.Response = autorest.Response{Response: response} + result, rerr := routetableClient.Get(context.TODO(), "rg", "rt1", "") + assert.Equal(t, expected, result) + assert.Nil(t, rerr) +} + func TestGetNotFound(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -79,24 +130,166 @@ func TestGetInternalError(t *testing.T) { assert.Equal(t, http.StatusInternalServerError, rerr.HTTPStatusCode) } +func TestGetNeverRateLimiter(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + rtGetErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider rate limited(%s) for operation %q", "read", "RouteTableGet"), + Retriable: true, + } + + armClient := mockarmclient.NewMockInterface(ctrl) + + routetableClient := getTestRouteTableClientWithNeverRateLimiter(armClient) + expected := network.RouteTable{} + result, rerr := routetableClient.Get(context.TODO(), "rg", "rt1", "") + assert.Equal(t, expected, result) + assert.Equal(t, rtGetErr, rerr) +} + +func TestGetRetryAfterReader(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + rtGetErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider throttled for operation %s with reason %q", "RouteTableGet", "client throttled"), + Retriable: true, + RetryAfter: getFutureTime(), + } + + armClient := mockarmclient.NewMockInterface(ctrl) + + routetableClient := getTestRouteTableClientWithRetryAfterReader(armClient) + expected := network.RouteTable{} + result, rerr := routetableClient.Get(context.TODO(), "rg", "rt1", "") + assert.Equal(t, expected, result) + assert.Equal(t, rtGetErr, rerr) +} + +func TestGetThrottle(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/routeTables/rt1" + 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) + + routetableClient := getTestRouteTableClient(armClient) + result, rerr := routetableClient.Get(context.TODO(), "rg", "rt1", "") + assert.Empty(t, result) + assert.Equal(t, throttleErr, rerr) +} + func TestCreateOrUpdate(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - lb := getTestRouteTable("rt1") + rt1 := getTestRouteTable("rt1") armClient := mockarmclient.NewMockInterface(ctrl) response := &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), } - armClient.EXPECT().PutResourceWithDecorators(gomock.Any(), to.String(lb.ID), lb, gomock.Any()).Return(response, nil).Times(1) + armClient.EXPECT().PutResourceWithDecorators(gomock.Any(), to.String(rt1.ID), rt1, gomock.Any()).Return(response, nil).Times(1) armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) rtClient := getTestRouteTableClient(armClient) - rerr := rtClient.CreateOrUpdate(context.TODO(), "rg", "rt1", lb, "") + rerr := rtClient.CreateOrUpdate(context.TODO(), "rg", "rt1", rt1, "*") assert.Nil(t, rerr) } +func TestCreateOrUpdateWithNeverRateLimiter(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + rcCreateOrUpdatetErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider rate limited(%s) for operation %q", "write", "RouteTableCreateOrUpdate"), + Retriable: true, + } + + rt1 := getTestRouteTable("rt1") + armClient := mockarmclient.NewMockInterface(ctrl) + + routetableClient := getTestRouteTableClientWithNeverRateLimiter(armClient) + rerr := routetableClient.CreateOrUpdate(context.TODO(), "rg", "rt1", rt1, "") + assert.Equal(t, rcCreateOrUpdatetErr, rerr) +} + +func TestCreateOrUpdateRetryAfterReader(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + rcCreateOrUpdateErr := &retry.Error{ + RawError: fmt.Errorf("azure cloud provider throttled for operation %s with reason %q", "RouteTableCreateOrUpdate", "client throttled"), + Retriable: true, + RetryAfter: getFutureTime(), + } + + rt1 := getTestRouteTable("rt1") + armClient := mockarmclient.NewMockInterface(ctrl) + + routetableClient := getTestRouteTableClientWithRetryAfterReader(armClient) + rerr := routetableClient.CreateOrUpdate(context.TODO(), "rg", "rt1", rt1, "") + assert.NotNil(t, rerr) + assert.Equal(t, rcCreateOrUpdateErr, 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), + } + + rt1 := getTestRouteTable("rt1") + armClient := mockarmclient.NewMockInterface(ctrl) + armClient.EXPECT().PutResourceWithDecorators(gomock.Any(), to.String(rt1.ID), rt1, gomock.Any()).Return(response, throttleErr).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + routetableClient := getTestRouteTableClient(armClient) + rerr := routetableClient.CreateOrUpdate(context.TODO(), "rg", "rt1", rt1, "") + assert.Equal(t, throttleErr, rerr) +} + +func TestCreateOrUpdateWithCreateOrUpdateResponderError(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + rt1 := getTestRouteTable("rt1") + armClient := mockarmclient.NewMockInterface(ctrl) + response := &http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + } + + armClient.EXPECT().PutResourceWithDecorators(gomock.Any(), to.String(rt1.ID), rt1, gomock.Any()).Return(response, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1) + + routetableClient := getTestRouteTableClient(armClient) + rerr := routetableClient.CreateOrUpdate(context.TODO(), "rg", "rt1", rt1, "") + assert.NotNil(t, rerr) +} + func getTestRouteTable(name string) network.RouteTable { return network.RouteTable{ ID: to.StringPtr(fmt.Sprintf("/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/routeTables/%s", name)), @@ -114,3 +307,27 @@ func getTestRouteTableClient(armClient armclient.Interface) *Client { rateLimiterWriter: rateLimiterWriter, } } + +func getTestRouteTableClientWithNeverRateLimiter(armClient armclient.Interface) *Client { + rateLimiterReader := flowcontrol.NewFakeNeverRateLimiter() + rateLimiterWriter := flowcontrol.NewFakeNeverRateLimiter() + return &Client{ + armClient: armClient, + subscriptionID: "subscriptionID", + rateLimiterReader: rateLimiterReader, + rateLimiterWriter: rateLimiterWriter, + } +} + +func getTestRouteTableClientWithRetryAfterReader(armClient armclient.Interface) *Client { + rateLimiterReader := flowcontrol.NewFakeAlwaysRateLimiter() + rateLimiterWriter := flowcontrol.NewFakeAlwaysRateLimiter() + return &Client{ + armClient: armClient, + subscriptionID: "subscriptionID", + rateLimiterReader: rateLimiterReader, + rateLimiterWriter: rateLimiterWriter, + RetryAfterReader: getFutureTime(), + RetryAfterWriter: getFutureTime(), + } +}