Enrich the unit tests for azure clients and azure zones.

This commit is contained in:
t-qini 2020-04-23 10:46:37 +08:00
parent 3fdceba86f
commit c4d0b7f61f
9 changed files with 721 additions and 11 deletions

View File

@ -24,6 +24,19 @@ import (
"net"
"net/http"
"testing"
"k8s.io/apimachinery/pkg/util/sets"
cloudprovider "k8s.io/cloud-provider"
"k8s.io/legacy-cloud-providers/azure/clients/vmclient/mockvmclient"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
"github.com/Azure/go-autorest/autorest/to"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)
const (
testAvailabilitySetNodeProviderID = "azure:///subscriptions/sub/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm-0"
)
func TestIsAvailabilityZone(t *testing.T) {
@ -91,6 +104,8 @@ func TestGetZone(t *testing.T) {
location string
faultDomain string
expected string
isNilResp bool
expectedErr error
}{
{
name: "GetZone should get real zone if only node's zone is set",
@ -117,6 +132,19 @@ func TestGetZone(t *testing.T) {
zone: "1",
expected: "eastus-1",
},
{
name: "GetZone should report an error if there is no `Compute` in the response",
isNilResp: true,
expectedErr: fmt.Errorf("failure of getting compute information from instance metadata"),
},
{
name: "GetZone should report an error if the zone is invalid",
zone: "a",
location: "eastus",
faultDomain: "99",
expected: "",
expectedErr: fmt.Errorf("failed to parse zone ID \"a\": strconv.Atoi: parsing \"a\": invalid syntax"),
},
}
for _, test := range testcases {
@ -125,9 +153,13 @@ func TestGetZone(t *testing.T) {
t.Errorf("Test [%s] unexpected error: %v", test.name, err)
}
respString := fmt.Sprintf(`{"compute":{"zone":"%s", "platformFaultDomain":"%s", "location":"%s"}}`, test.zone, test.faultDomain, test.location)
if test.isNilResp {
respString = "{}"
}
mux := http.NewServeMux()
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, fmt.Sprintf(`{"compute":{"zone":"%s", "platformFaultDomain":"%s", "location":"%s"}}`, test.zone, test.faultDomain, test.location))
fmt.Fprint(w, respString)
}))
go func() {
http.Serve(listener, mux)
@ -141,13 +173,72 @@ func TestGetZone(t *testing.T) {
zone, err := cloud.GetZone(context.Background())
if err != nil {
t.Errorf("Test [%s] unexpected error: %v", test.name, err)
if test.expectedErr == nil {
t.Errorf("Test [%s] unexpected error: %v", test.name, err)
} else {
assert.Equal(t, test.expectedErr, err)
}
}
if zone.FailureDomain != test.expected {
t.Errorf("Test [%s] unexpected zone: %s, expected %q", test.name, zone.FailureDomain, test.expected)
}
if zone.Region != cloud.Location {
if err == nil && zone.Region != cloud.Location {
t.Errorf("Test [%s] unexpected region: %s, expected: %s", test.name, zone.Region, cloud.Location)
}
}
}
func TestMakeZone(t *testing.T) {
az := &Cloud{}
zone := az.makeZone("EASTUS", 2)
assert.Equal(t, "eastus-2", zone)
}
func TestGetZoneByProviderID(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
az := GetTestCloud(ctrl)
zone, err := az.GetZoneByProviderID(context.Background(), "")
assert.Equal(t, errNodeNotInitialized, err)
assert.Equal(t, cloudprovider.Zone{}, zone)
zone, err = az.GetZoneByProviderID(context.Background(), "invalid/id")
assert.NoError(t, err)
assert.Equal(t, cloudprovider.Zone{}, zone)
mockVMClient := az.VirtualMachinesClient.(*mockvmclient.MockInterface)
mockVMClient.EXPECT().Get(gomock.Any(), az.ResourceGroup, "vm-0", gomock.Any()).Return(compute.VirtualMachine{
Zones: &[]string{"1"},
Location: to.StringPtr("eastus"),
}, nil)
zone, err = az.GetZoneByProviderID(context.Background(), testAvailabilitySetNodeProviderID)
assert.NoError(t, err)
assert.Equal(t, cloudprovider.Zone{
FailureDomain: "eastus-1",
Region: "eastus",
}, zone)
}
func TestAvailabilitySetGetZoneByNodeName(t *testing.T) {
az := &Cloud{
unmanagedNodes: sets.String{"vm-0": sets.Empty{}},
nodeInformerSynced: func() bool {
return true
},
}
zone, err := az.GetZoneByNodeName(context.Background(), "vm-0")
assert.NoError(t, err)
assert.Equal(t, cloudprovider.Zone{}, zone)
az = &Cloud{
unmanagedNodes: sets.String{"vm-0": sets.Empty{}},
nodeInformerSynced: func() bool {
return false
},
}
zone, err = az.GetZoneByNodeName(context.Background(), "vm-0")
assert.Equal(t, fmt.Errorf("node informer is not synced when trying to GetUnmanagedNodes"), err)
assert.Equal(t, cloudprovider.Zone{}, zone)
}

View File

@ -285,7 +285,7 @@ func (c *Client) SendAsync(ctx context.Context, request *http.Request) (*azure.F
future, err := azure.NewFutureFromResponse(asyncResponse)
if err != nil {
klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "sendAsync.responed", request.URL.String(), err)
klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "sendAsync.respond", request.URL.String(), err)
return nil, asyncResponse, retry.GetError(asyncResponse, err)
}
@ -337,7 +337,7 @@ func (c *Client) PutResource(ctx context.Context, resourceID string, parameters
}
// PutResources puts a list of resources from resources map[resourceID]parameters.
// Those resources sync requests are sequential while async requests are concurent. It 's especially
// Those resources sync requests are sequential while async requests are concurrent. It's especially
// useful when the ARM API doesn't support concurrent requests.
func (c *Client) PutResources(ctx context.Context, resources map[string]interface{}) map[string]*PutResourcesResponse {
if len(resources) == 0 {

View File

@ -21,6 +21,7 @@ package armclient
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
@ -31,6 +32,10 @@ import (
"k8s.io/legacy-cloud-providers/azure/retry"
)
const (
testResourceID = "/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/testPIP"
)
func TestSend(t *testing.T) {
count := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -84,7 +89,7 @@ func TestSendFailure(t *testing.T) {
}
ctx := context.Background()
request, err := armClient.PrepareGetRequest(ctx, decorators...)
request, err := armClient.PreparePatchRequest(ctx, decorators...)
assert.NoError(t, err)
response, rerr := armClient.Send(ctx, request)
@ -128,7 +133,6 @@ func TestSendAsync(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
http.Error(w, "failed", http.StatusForbidden)
}))
backoff := &retry.Backoff{Steps: 1}
@ -157,6 +161,35 @@ func TestSendAsync(t *testing.T) {
assert.Equal(t, false, rerr.Retriable)
}
func TestSendAsyncSuccess(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
backoff := &retry.Backoff{Steps: 1}
armClient := New(nil, server.URL, "test", "2019-01-01", "eastus", backoff)
armClient.client.RetryDuration = time.Millisecond * 1
pathParameters := map[string]interface{}{
"resourceGroupName": autorest.Encode("path", "testgroup"),
"subscriptionId": autorest.Encode("path", "testid"),
"resourceName": autorest.Encode("path", "testname"),
}
decorators := []autorest.PrepareDecorator{
autorest.WithPathParameters(
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/vNets/{resourceName}", pathParameters),
}
ctx := context.Background()
request, err := armClient.PreparePostRequest(ctx, decorators...)
assert.NoError(t, err)
future, response, rerr := armClient.SendAsync(ctx, request)
assert.Nil(t, rerr)
assert.NotNil(t, response)
assert.NotNil(t, future)
}
func TestNormalizeAzureRegion(t *testing.T) {
tests := []struct {
region string
@ -190,6 +223,64 @@ func TestNormalizeAzureRegion(t *testing.T) {
}
}
func TestGetResource(t *testing.T) {
expectedURI := "/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/testPIP?%24expand=data&api-version=2019-01-01"
count := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
assert.Equal(t, expectedURI, r.URL.String())
w.WriteHeader(http.StatusOK)
w.Write([]byte("{data: testPIP}"))
count++
}))
backoff := &retry.Backoff{Steps: 1}
armClient := New(nil, server.URL, "test", "2019-01-01", "eastus", backoff)
armClient.client.RetryDuration = time.Millisecond * 1
ctx := context.Background()
response, rerr := armClient.GetResource(ctx, testResourceID, "data")
byteResponseBody, _ := ioutil.ReadAll(response.Body)
stringResponseBody := string(byteResponseBody)
assert.Nil(t, rerr)
assert.Equal(t, "{data: testPIP}", stringResponseBody)
assert.Equal(t, 1, count)
}
func TestGetResourceWithDecorators(t *testing.T) {
expectedURI := "/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/testPIP?api-version=2019-01-01&param1=value1&param2=value2"
count := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
assert.Equal(t, expectedURI, r.URL.String())
w.WriteHeader(http.StatusOK)
w.Write([]byte("{data: testPIP}"))
count++
}))
backoff := &retry.Backoff{Steps: 1}
armClient := New(nil, server.URL, "test", "2019-01-01", "eastus", backoff)
armClient.client.RetryDuration = time.Millisecond * 1
params := map[string]interface{}{
"param1": "value1",
"param2": "value2",
}
decorators := []autorest.PrepareDecorator{
autorest.WithQueryParameters(params),
}
ctx := context.Background()
response, rerr := armClient.GetResourceWithDecorators(ctx, testResourceID, decorators)
byteResponseBody, _ := ioutil.ReadAll(response.Body)
stringResponseBody := string(byteResponseBody)
assert.Nil(t, rerr)
assert.Equal(t, "{data: testPIP}", stringResponseBody)
assert.Equal(t, 1, count)
}
func TestPutResource(t *testing.T) {
expectedURI := "/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/testPIP?api-version=2019-01-01"
operationURI := "/subscriptions/subscription/providers/Microsoft.Network/locations/eastus/operations/op?api-version=2019-01-01"
@ -225,13 +316,89 @@ func TestPutResource(t *testing.T) {
armClient.client.RetryDuration = time.Millisecond * 1
ctx := context.Background()
response, rerr := armClient.PutResource(ctx, "/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/testPIP", nil)
response, rerr := armClient.PutResource(ctx, testResourceID, nil)
assert.Equal(t, 1, count)
assert.Nil(t, response)
assert.NotNil(t, rerr)
assert.Equal(t, true, rerr.Retriable)
}
func TestPutResources(t *testing.T) {
serverFuncs := []func(rw http.ResponseWriter, req *http.Request){
func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, "PUT", req.Method)
rw.Header().Set(http.CanonicalHeaderKey("Azure-AsyncOperation"),
fmt.Sprintf("http://%s%s", req.Host, "/id/1?api-version=2019-01-01"))
rw.WriteHeader(http.StatusCreated)
},
func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, "PUT", req.Method)
rw.Header().Set(http.CanonicalHeaderKey("Azure-AsyncOperation"),
fmt.Sprintf("http://%s%s", req.Host, "/id/2?api-version=2019-01-01"))
rw.WriteHeader(http.StatusInternalServerError)
},
func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, "GET", req.Method)
rw.WriteHeader(http.StatusOK)
rw.Write([]byte(`{"error":{"code":"InternalServerError"},"status":"Failed"}`))
},
func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, "GET", req.Method)
rw.WriteHeader(http.StatusOK)
rw.Write([]byte(`{"error":{"code":"InternalServerError"},"status":"Failed"}`))
},
}
i, total := 0, 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
serverFuncs[i](w, r)
i++
if i > 3 {
i = 3
}
total++
}))
backoff := &retry.Backoff{Steps: 1}
armClient := New(nil, server.URL, "test", "2019-01-01", "eastus", backoff)
armClient.client.RetryDuration = time.Millisecond * 1
ctx := context.Background()
resources := map[string]interface{}{
"/id/1": nil,
"/id/2": nil,
}
responses := armClient.PutResources(ctx, nil)
assert.Nil(t, responses)
responses = armClient.PutResources(ctx, resources)
assert.NotNil(t, responses)
assert.Equal(t, 3, total)
}
func TestPutResourceAsync(t *testing.T) {
count := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
http.Error(w, "failed", http.StatusInternalServerError)
}))
backoff := &retry.Backoff{Steps: 3}
armClient := New(nil, server.URL, "test", "2019-01-01", "eastus", backoff)
armClient.client.RetryDuration = time.Millisecond * 1
ctx := context.Background()
resourceID := testResourceID
future, rerr := armClient.PutResourceAsync(ctx, resourceID, "")
assert.Equal(t, 3, count)
assert.Nil(t, future)
assert.NotNil(t, rerr)
assert.Equal(t, true, rerr.Retriable)
}
func TestDeleteResourceAsync(t *testing.T) {
count := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -244,10 +411,146 @@ func TestDeleteResourceAsync(t *testing.T) {
armClient.client.RetryDuration = time.Millisecond * 1
ctx := context.Background()
resourceID := "/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/testPIP"
resourceID := testResourceID
future, rerr := armClient.DeleteResourceAsync(ctx, resourceID, "")
assert.Equal(t, 3, count)
assert.Nil(t, future)
assert.NotNil(t, rerr)
assert.Equal(t, true, rerr.Retriable)
}
func TestPatchResource(t *testing.T) {
expectedURI := "/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/testPIP?api-version=2019-01-01"
operationURI := "/subscriptions/subscription/providers/Microsoft.Network/locations/eastus/operations/op?api-version=2019-01-01"
handlers := []func(http.ResponseWriter, *http.Request){
func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, "PATCH", req.Method)
assert.Equal(t, expectedURI, req.URL.String())
rw.Header().Set(http.CanonicalHeaderKey("Azure-AsyncOperation"),
fmt.Sprintf("http://%s%s", req.Host, operationURI))
rw.WriteHeader(http.StatusCreated)
},
func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, "GET", req.Method)
assert.Equal(t, operationURI, req.URL.String())
rw.WriteHeader(http.StatusOK)
rw.Write([]byte(`{"error":{"code":"InternalServerError"},"status":"Failed"}`))
},
}
count := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handlers[count](w, r)
count++
if count > 1 {
count = 1
}
}))
backoff := &retry.Backoff{Steps: 1}
armClient := New(nil, server.URL, "test", "2019-01-01", "eastus", backoff)
armClient.client.RetryDuration = time.Millisecond * 1
ctx := context.Background()
response, rerr := armClient.PatchResource(ctx, testResourceID, nil)
assert.Equal(t, 1, count)
assert.Nil(t, response)
assert.NotNil(t, rerr)
assert.Equal(t, true, rerr.Retriable)
}
func TestPostResource(t *testing.T) {
count := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
http.Error(w, "failed", http.StatusInternalServerError)
}))
backoff := &retry.Backoff{Steps: 3}
armClient := New(nil, server.URL, "test", "2019-01-01", "eastus", backoff)
armClient.client.RetryDuration = time.Millisecond * 1
ctx := context.Background()
resourceID := testResourceID
future, rerr := armClient.PostResource(ctx, resourceID, "post", "")
assert.Equal(t, 3, count)
assert.NotNil(t, future)
assert.NotNil(t, rerr)
assert.Equal(t, true, rerr.Retriable)
}
func TestDeleteResource(t *testing.T) {
count := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
http.Error(w, "failed", http.StatusInternalServerError)
}))
backoff := &retry.Backoff{Steps: 3}
armClient := New(nil, server.URL, "test", "2019-01-01", "eastus", backoff)
armClient.client.RetryDuration = time.Millisecond * 1
ctx := context.Background()
resourceID := testResourceID
rerr := armClient.DeleteResource(ctx, resourceID, "")
assert.Equal(t, 3, count)
assert.NotNil(t, rerr)
assert.Equal(t, true, rerr.Retriable)
}
func TestHeadResource(t *testing.T) {
count := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
http.Error(w, "failed", http.StatusInternalServerError)
}))
backoff := &retry.Backoff{Steps: 3}
armClient := New(nil, server.URL, "test", "2019-01-01", "eastus", backoff)
armClient.client.RetryDuration = time.Millisecond * 1
ctx := context.Background()
resourceID := testResourceID
response, rerr := armClient.HeadResource(ctx, resourceID)
assert.Equal(t, 3, count)
assert.NotNil(t, response)
assert.NotNil(t, rerr)
assert.Equal(t, true, rerr.Retriable)
}
func TestGetResourceID(t *testing.T) {
expectedResourceID := "/subscriptions/sub/resourceGroups/rg/providers/type/name"
resourceID := GetResourceID("sub", "rg", "type", "name")
assert.Equal(t, expectedResourceID, resourceID)
}
func TestGetChildResourceID(t *testing.T) {
expectedResourceID := "/subscriptions/sub/resourceGroups/rg/providers/type/name-1/name-2/name-3"
resourceID := GetChildResourceID("sub", "rg", "type", "name-1", "name-2", "name-3")
assert.Equal(t, expectedResourceID, resourceID)
}
func TestGetChildResourcesListID(t *testing.T) {
expectedResourceID := "/subscriptions/sub/resourceGroups/rg/providers/type/name-1/name-2"
resourceID := GetChildResourcesListID("sub", "rg", "type", "name-1", "name-2")
assert.Equal(t, expectedResourceID, resourceID)
}
func TestGetProviderResourceID(t *testing.T) {
expectedResourceID := "/subscriptions/sub/providers/namespace"
resourceID := GetProviderResourceID("sub", "namespace")
assert.Equal(t, expectedResourceID, resourceID)
}
func TestGetProviderResourcesListID(t *testing.T) {
expectedResourceID := "/subscriptions/sub/providers"
resourceID := GetProviderResourcesListID("sub")
assert.Equal(t, expectedResourceID, resourceID)
}

View File

@ -31,6 +31,7 @@ go_test(
"//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/compute/mgmt/2019-12-01/compute:go_default_library",
"//vendor/github.com/Azure/go-autorest/autorest:go_default_library",
"//vendor/github.com/Azure/go-autorest/autorest/to:go_default_library",

View File

@ -25,6 +25,7 @@ import (
"io/ioutil"
"net/http"
"testing"
"time"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
"github.com/Azure/go-autorest/autorest"
@ -35,8 +36,30 @@ import (
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},
}
diskClient := New(config)
assert.Equal(t, "sub", diskClient.subscriptionID)
assert.NotEmpty(t, diskClient.rateLimiterReader)
assert.NotEmpty(t, diskClient.rateLimiterWriter)
}
func TestGetNotFound(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@ -79,6 +102,31 @@ 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.Compute/disks/disk1"
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)
diskClient := getTestDiskClient(armClient)
result, rerr := diskClient.Get(context.TODO(), "rg", "disk1")
assert.Empty(t, result)
assert.Equal(t, throttleErr, rerr)
}
func TestCreateOrUpdate(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@ -95,6 +143,22 @@ func TestCreateOrUpdate(t *testing.T) {
diskClient := getTestDiskClient(armClient)
rerr := diskClient.CreateOrUpdate(context.TODO(), "rg", "disk1", disk)
assert.Nil(t, rerr)
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().PutResource(gomock.Any(), to.String(disk.ID), disk).Return(response, throttleErr).Times(1)
armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
rerr = diskClient.CreateOrUpdate(context.TODO(), "rg", "disk1", disk)
assert.Equal(t, throttleErr, rerr)
}
func TestDelete(t *testing.T) {
@ -108,6 +172,16 @@ func TestDelete(t *testing.T) {
diskClient := getTestDiskClient(armClient)
rerr := diskClient.Delete(context.TODO(), "rg", "disk1")
assert.Nil(t, rerr)
throttleErr := &retry.Error{
HTTPStatusCode: http.StatusTooManyRequests,
RawError: fmt.Errorf("error"),
Retriable: true,
RetryAfter: time.Unix(100, 0),
}
armClient.EXPECT().DeleteResource(gomock.Any(), to.String(r.ID), "").Return(throttleErr).Times(1)
rerr = diskClient.Delete(context.TODO(), "rg", "disk1")
assert.Equal(t, throttleErr, rerr)
}
func getTestDisk(name string) compute.Disk {

View File

@ -31,6 +31,7 @@ go_test(
"//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",

View File

@ -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"
@ -35,8 +36,30 @@ import (
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},
}
interfaceClient := New(config)
assert.Equal(t, "sub", interfaceClient.subscriptionID)
assert.NotEmpty(t, interfaceClient.rateLimiterReader)
assert.NotEmpty(t, interfaceClient.rateLimiterWriter)
}
func TestGetNotFound(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@ -79,6 +102,31 @@ 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/networkInterfaces/nic1"
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)
nicClient := getTestInterfaceClient(armClient)
result, rerr := nicClient.Get(context.TODO(), "rg", "nic1", "")
assert.Empty(t, result)
assert.Equal(t, throttleErr, rerr)
}
func TestGetVirtualMachineScaleSetNetworkInterface(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@ -101,6 +149,23 @@ func TestGetVirtualMachineScaleSetNetworkInterface(t *testing.T) {
result, rerr := nicClient.GetVirtualMachineScaleSetNetworkInterface(context.TODO(), "rg", "vmss", "0", "nic1", "")
assert.Equal(t, expected, result)
assert.Nil(t, rerr)
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().GetResourceWithDecorators(gomock.Any(), resourceID, gomock.Any()).Return(response, throttleErr).Times(1)
armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
result, rerr = nicClient.GetVirtualMachineScaleSetNetworkInterface(context.TODO(), "rg", "vmss", "0", "nic1", "test")
assert.Empty(t, result)
assert.Equal(t, throttleErr, rerr)
}
func TestCreateOrUpdate(t *testing.T) {
@ -119,6 +184,22 @@ func TestCreateOrUpdate(t *testing.T) {
nicClient := getTestInterfaceClient(armClient)
rerr := nicClient.CreateOrUpdate(context.TODO(), "rg", "nic1", testInterface)
assert.Nil(t, rerr)
response = &http.Response{
StatusCode: http.StatusNoContent,
Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))),
}
noContentErr := &retry.Error{
HTTPStatusCode: http.StatusNoContent,
RawError: fmt.Errorf("error"),
Retriable: true,
RetryAfter: time.Unix(100, 0),
}
armClient.EXPECT().PutResource(gomock.Any(), to.String(testInterface.ID), testInterface).Return(response, noContentErr).Times(1)
armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
rerr = nicClient.CreateOrUpdate(context.TODO(), "rg", "nic1", testInterface)
assert.Equal(t, noContentErr, rerr)
}
func TestDelete(t *testing.T) {
@ -129,9 +210,20 @@ func TestDelete(t *testing.T) {
armClient := mockarmclient.NewMockInterface(ctrl)
armClient.EXPECT().DeleteResource(gomock.Any(), to.String(r.ID), "").Return(nil).Times(1)
diskClient := getTestInterfaceClient(armClient)
rerr := diskClient.Delete(context.TODO(), "rg", "interface1")
nicClient := getTestInterfaceClient(armClient)
rerr := nicClient.Delete(context.TODO(), "rg", "interface1")
assert.Nil(t, rerr)
noContentErr := &retry.Error{
HTTPStatusCode: http.StatusNoContent,
RawError: fmt.Errorf("error"),
Retriable: true,
RetryAfter: time.Unix(100, 0),
}
armClient.EXPECT().DeleteResource(gomock.Any(), to.String(r.ID), "").Return(noContentErr).Times(1)
rerr = nicClient.Delete(context.TODO(), "rg", "interface1")
assert.Equal(t, noContentErr, rerr)
}
func getTestInterface(name string) network.Interface {

View File

@ -32,6 +32,7 @@ go_test(
"//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",

View File

@ -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"
@ -36,8 +37,30 @@ import (
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},
}
lbClient := New(config)
assert.Equal(t, "sub", lbClient.subscriptionID)
assert.NotEmpty(t, lbClient.rateLimiterReader)
assert.NotEmpty(t, lbClient.rateLimiterWriter)
}
func TestGetNotFound(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@ -80,6 +103,31 @@ 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/loadBalancers/lb1"
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)
lbClient := getTestLoadBalancerClient(armClient)
result, rerr := lbClient.Get(context.TODO(), "rg", "lb1", "")
assert.Empty(t, result)
assert.Equal(t, throttleErr, rerr)
}
func TestList(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@ -100,6 +148,74 @@ func TestList(t *testing.T) {
result, rerr := lbClient.List(context.TODO(), "rg")
assert.Nil(t, rerr)
assert.Equal(t, 3, len(result))
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)
_, rerr = lbClient.List(context.TODO(), "rg")
assert.Equal(t, throttleErr, 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.LoadBalancerListResult{
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())
}
lbClient := getTestLoadBalancerClient(armClient)
result, err := lbClient.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 TestCreateOrUpdate(t *testing.T) {
@ -120,6 +236,37 @@ func TestCreateOrUpdate(t *testing.T) {
assert.Nil(t, rerr)
}
func TestDelete(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
tests := []struct {
description string
armClientErr *retry.Error
expectedErr *retry.Error
}{
{
description: "Delete should report the throttling error",
armClientErr: &retry.Error{HTTPStatusCode: http.StatusTooManyRequests},
expectedErr: &retry.Error{HTTPStatusCode: http.StatusTooManyRequests},
},
{
description: "Delete should not report any error if there's no error from arm client",
},
}
lb := getTestLoadBalancer("lb1")
for _, test := range tests {
armClient := mockarmclient.NewMockInterface(ctrl)
armClient.EXPECT().DeleteResource(gomock.Any(), to.String(lb.ID), "").Return(test.armClientErr)
lbClient := getTestLoadBalancerClient(armClient)
rerr := lbClient.Delete(context.TODO(), "rg", "lb1")
assert.Equal(t, test.expectedErr, rerr)
}
}
func getTestLoadBalancer(name string) network.LoadBalancer {
return network.LoadBalancer{
ID: to.StringPtr(fmt.Sprintf("/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/loadBalancers/%s", name)),