diff --git a/Godeps/LICENSES b/Godeps/LICENSES index d7bfc58dbe7..cf0cd4334dd 100644 --- a/Godeps/LICENSES +++ b/Godeps/LICENSES @@ -1547,6 +1547,205 @@ THE SOFTWARE. ================================================================================ +================================================================================ += vendor/github.com/Azure/go-autorest/autorest/mocks licensed under: = + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015 Microsoft Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + += vendor/github.com/Azure/go-autorest/autorest/mocks/LICENSE a250e5ac3848f2acadb5adcb9555c18b +================================================================================ + + ================================================================================ = vendor/github.com/Azure/go-autorest/autorest/to licensed under: = diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/BUILD b/staging/src/k8s.io/legacy-cloud-providers/azure/BUILD index 8b1a08a4324..4ac822a5a4b 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/BUILD +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/BUILD @@ -68,13 +68,13 @@ go_library( "//staging/src/k8s.io/component-base/metrics:go_default_library", "//staging/src/k8s.io/component-base/metrics/legacyregistry:go_default_library", "//staging/src/k8s.io/legacy-cloud-providers/azure/auth:go_default_library", + "//staging/src/k8s.io/legacy-cloud-providers/azure/clients: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-07-01/compute: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/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage:go_default_library", "//vendor/github.com/Azure/azure-sdk-for-go/storage:go_default_library", "//vendor/github.com/Azure/go-autorest/autorest:go_default_library", - "//vendor/github.com/Azure/go-autorest/autorest/adal:go_default_library", "//vendor/github.com/Azure/go-autorest/autorest/azure:go_default_library", "//vendor/github.com/Azure/go-autorest/autorest/to:go_default_library", "//vendor/github.com/rubiojr/go-vhd/vhd:go_default_library", @@ -118,6 +118,7 @@ go_test( "//staging/src/k8s.io/cloud-provider:go_default_library", "//staging/src/k8s.io/cloud-provider/service/helpers:go_default_library", "//staging/src/k8s.io/legacy-cloud-providers/azure/auth:go_default_library", + "//staging/src/k8s.io/legacy-cloud-providers/azure/clients: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-07-01/compute:go_default_library", "//vendor/github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network:go_default_library", @@ -141,6 +142,7 @@ filegroup( srcs = [ ":package-srcs", "//staging/src/k8s.io/legacy-cloud-providers/azure/auth:all-srcs", + "//staging/src/k8s.io/legacy-cloud-providers/azure/clients:all-srcs", "//staging/src/k8s.io/legacy-cloud-providers/azure/retry:all-srcs", ], tags = ["automanaged"], diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/auth/BUILD b/staging/src/k8s.io/legacy-cloud-providers/azure/auth/BUILD index f35a36d4e0e..36c4dabe596 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/auth/BUILD +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/auth/BUILD @@ -8,7 +8,10 @@ load( go_library( name = "go_default_library", - srcs = ["azure_auth.go"], + srcs = [ + "azure_auth.go", + "doc.go", + ], importmap = "k8s.io/kubernetes/vendor/k8s.io/legacy-cloud-providers/azure/auth", importpath = "k8s.io/legacy-cloud-providers/azure/auth", visibility = ["//visibility:public"], diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/auth/doc.go b/staging/src/k8s.io/legacy-cloud-providers/azure/auth/doc.go new file mode 100644 index 00000000000..073f70a4182 --- /dev/null +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/auth/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package auth provides a general libraty to authorize Azure ARM clients. +package auth // import "k8s.io/legacy-cloud-providers/azure/auth" diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/azure.go b/staging/src/k8s.io/legacy-cloud-providers/azure/azure.go index dbcd998710f..cb66e9fc64c 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/azure.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/azure.go @@ -45,6 +45,8 @@ import ( cloudprovider "k8s.io/cloud-provider" "k8s.io/klog" "k8s.io/legacy-cloud-providers/azure/auth" + azclients "k8s.io/legacy-cloud-providers/azure/clients" + "k8s.io/legacy-cloud-providers/azure/retry" "sigs.k8s.io/yaml" ) @@ -448,13 +450,23 @@ func (az *Cloud) InitializeCloudFromConfig(config *Config, fromSecret bool) erro } // Initialize Azure clients. - azClientConfig := &azClientConfig{ - subscriptionID: config.SubscriptionID, - resourceManagerEndpoint: env.ResourceManagerEndpoint, - servicePrincipalToken: servicePrincipalToken, + azClientConfig := &azclients.ClientConfig{ + Location: config.Location, + SubscriptionID: config.SubscriptionID, + ResourceManagerEndpoint: env.ResourceManagerEndpoint, + ServicePrincipalToken: servicePrincipalToken, CloudProviderBackoffRetries: config.CloudProviderBackoffRetries, CloudProviderBackoffDuration: config.CloudProviderBackoffDuration, ShouldOmitCloudProviderBackoff: config.shouldOmitCloudProviderBackoff(), + Backoff: &retry.Backoff{Steps: 1}, + } + if config.CloudProviderBackoff { + azClientConfig.Backoff = &retry.Backoff{ + Steps: config.CloudProviderBackoffRetries, + Factor: config.CloudProviderBackoffExponent, + Duration: time.Duration(config.CloudProviderBackoffDuration) * time.Second, + Jitter: config.CloudProviderBackoffJitter, + } } az.DisksClient = newAzDisksClient(azClientConfig.WithRateLimiter(config.DiskRateLimit)) az.SnapshotsClient = newSnapshotsClient(azClientConfig.WithRateLimiter(config.SnapshotRateLimit)) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/azure_client.go b/staging/src/k8s.io/legacy-cloud-providers/azure/azure_client.go index 9fc94a1d5c4..9caf2247869 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/azure_client.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/azure_client.go @@ -28,10 +28,10 @@ import ( "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage" "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/adal" "k8s.io/client-go/util/flowcontrol" "k8s.io/klog" + azclients "k8s.io/legacy-cloud-providers/azure/clients" "k8s.io/legacy-cloud-providers/azure/retry" ) @@ -145,24 +145,6 @@ type VirtualMachineSizesClient interface { List(ctx context.Context, location string) (result compute.VirtualMachineSizeListResult, rerr *retry.Error) } -// azClientConfig contains all essential information to create an Azure client. -type azClientConfig struct { - subscriptionID string - resourceManagerEndpoint string - servicePrincipalToken *adal.ServicePrincipalToken - rateLimitConfig *RateLimitConfig - - CloudProviderBackoffRetries int - CloudProviderBackoffDuration int - ShouldOmitCloudProviderBackoff bool -} - -// WithRateLimiter returns azClientConfig with rateLimitConfig set. -func (cfg *azClientConfig) WithRateLimiter(rl *RateLimitConfig) *azClientConfig { - cfg.rateLimitConfig = rl - return cfg -} - // azVirtualMachinesClient implements VirtualMachinesClient. type azVirtualMachinesClient struct { client compute.VirtualMachinesClient @@ -174,10 +156,10 @@ func getContextWithCancel() (context.Context, context.CancelFunc) { return context.WithCancel(context.Background()) } -func newAzVirtualMachinesClient(config *azClientConfig) *azVirtualMachinesClient { - virtualMachinesClient := compute.NewVirtualMachinesClient(config.subscriptionID) - virtualMachinesClient.BaseURI = config.resourceManagerEndpoint - virtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzVirtualMachinesClient(config *azclients.ClientConfig) *azVirtualMachinesClient { + virtualMachinesClient := compute.NewVirtualMachinesClient(config.SubscriptionID) + virtualMachinesClient.BaseURI = config.ResourceManagerEndpoint + virtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) virtualMachinesClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { virtualMachinesClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -186,12 +168,12 @@ func newAzVirtualMachinesClient(config *azClientConfig) *azVirtualMachinesClient configureUserAgent(&virtualMachinesClient.Client) klog.V(2).Infof("Azure VirtualMachinesClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure VirtualMachinesClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azVirtualMachinesClient{ rateLimiterReader: rateLimiterReader, rateLimiterWriter: rateLimiterWriter, @@ -302,10 +284,10 @@ type azInterfacesClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzInterfacesClient(config *azClientConfig) *azInterfacesClient { - interfacesClient := network.NewInterfacesClient(config.subscriptionID) - interfacesClient.BaseURI = config.resourceManagerEndpoint - interfacesClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzInterfacesClient(config *azclients.ClientConfig) *azInterfacesClient { + interfacesClient := network.NewInterfacesClient(config.SubscriptionID) + interfacesClient.BaseURI = config.ResourceManagerEndpoint + interfacesClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) interfacesClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { interfacesClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -314,12 +296,12 @@ func newAzInterfacesClient(config *azClientConfig) *azInterfacesClient { configureUserAgent(&interfacesClient.Client) klog.V(2).Infof("Azure InterfacesClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure InterfacesClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azInterfacesClient{ rateLimiterReader: rateLimiterReader, rateLimiterWriter: rateLimiterWriter, @@ -394,10 +376,10 @@ type azLoadBalancersClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzLoadBalancersClient(config *azClientConfig) *azLoadBalancersClient { - loadBalancerClient := network.NewLoadBalancersClient(config.subscriptionID) - loadBalancerClient.BaseURI = config.resourceManagerEndpoint - loadBalancerClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzLoadBalancersClient(config *azclients.ClientConfig) *azLoadBalancersClient { + loadBalancerClient := network.NewLoadBalancersClient(config.SubscriptionID) + loadBalancerClient.BaseURI = config.ResourceManagerEndpoint + loadBalancerClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) loadBalancerClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { loadBalancerClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -406,12 +388,12 @@ func newAzLoadBalancersClient(config *azClientConfig) *azLoadBalancersClient { configureUserAgent(&loadBalancerClient.Client) klog.V(2).Infof("Azure LoadBalancersClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure LoadBalancersClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azLoadBalancersClient{ rateLimiterReader: rateLimiterReader, rateLimiterWriter: rateLimiterWriter, @@ -552,10 +534,10 @@ type azPublicIPAddressesClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzPublicIPAddressesClient(config *azClientConfig) *azPublicIPAddressesClient { - publicIPAddressClient := network.NewPublicIPAddressesClient(config.subscriptionID) - publicIPAddressClient.BaseURI = config.resourceManagerEndpoint - publicIPAddressClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzPublicIPAddressesClient(config *azclients.ClientConfig) *azPublicIPAddressesClient { + publicIPAddressClient := network.NewPublicIPAddressesClient(config.SubscriptionID) + publicIPAddressClient.BaseURI = config.ResourceManagerEndpoint + publicIPAddressClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) publicIPAddressClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { publicIPAddressClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -564,12 +546,12 @@ func newAzPublicIPAddressesClient(config *azClientConfig) *azPublicIPAddressesCl configureUserAgent(&publicIPAddressClient.Client) klog.V(2).Infof("Azure PublicIPAddressesClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure PublicIPAddressesClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azPublicIPAddressesClient{ rateLimiterReader: rateLimiterReader, rateLimiterWriter: rateLimiterWriter, @@ -696,10 +678,10 @@ type azSubnetsClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzSubnetsClient(config *azClientConfig) *azSubnetsClient { - subnetsClient := network.NewSubnetsClient(config.subscriptionID) - subnetsClient.BaseURI = config.resourceManagerEndpoint - subnetsClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzSubnetsClient(config *azclients.ClientConfig) *azSubnetsClient { + subnetsClient := network.NewSubnetsClient(config.SubscriptionID) + subnetsClient.BaseURI = config.ResourceManagerEndpoint + subnetsClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) subnetsClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { subnetsClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -708,12 +690,12 @@ func newAzSubnetsClient(config *azClientConfig) *azSubnetsClient { configureUserAgent(&subnetsClient.Client) klog.V(2).Infof("Azure SubnetsClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure SubnetsClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azSubnetsClient{ client: subnetsClient, rateLimiterReader: rateLimiterReader, @@ -821,10 +803,10 @@ type azSecurityGroupsClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzSecurityGroupsClient(config *azClientConfig) *azSecurityGroupsClient { - securityGroupsClient := network.NewSecurityGroupsClient(config.subscriptionID) - securityGroupsClient.BaseURI = config.resourceManagerEndpoint - securityGroupsClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzSecurityGroupsClient(config *azclients.ClientConfig) *azSecurityGroupsClient { + securityGroupsClient := network.NewSecurityGroupsClient(config.SubscriptionID) + securityGroupsClient.BaseURI = config.ResourceManagerEndpoint + securityGroupsClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) securityGroupsClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { securityGroupsClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -833,12 +815,12 @@ func newAzSecurityGroupsClient(config *azClientConfig) *azSecurityGroupsClient { configureUserAgent(&securityGroupsClient.Client) klog.V(2).Infof("Azure SecurityGroupsClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure SecurityGroupsClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azSecurityGroupsClient{ client: securityGroupsClient, rateLimiterReader: rateLimiterReader, @@ -978,10 +960,10 @@ type azVirtualMachineScaleSetsClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzVirtualMachineScaleSetsClient(config *azClientConfig) *azVirtualMachineScaleSetsClient { - virtualMachineScaleSetsClient := compute.NewVirtualMachineScaleSetsClient(config.subscriptionID) - virtualMachineScaleSetsClient.BaseURI = config.resourceManagerEndpoint - virtualMachineScaleSetsClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzVirtualMachineScaleSetsClient(config *azclients.ClientConfig) *azVirtualMachineScaleSetsClient { + virtualMachineScaleSetsClient := compute.NewVirtualMachineScaleSetsClient(config.SubscriptionID) + virtualMachineScaleSetsClient.BaseURI = config.ResourceManagerEndpoint + virtualMachineScaleSetsClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) virtualMachineScaleSetsClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { virtualMachineScaleSetsClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -990,12 +972,12 @@ func newAzVirtualMachineScaleSetsClient(config *azClientConfig) *azVirtualMachin configureUserAgent(&virtualMachineScaleSetsClient.Client) klog.V(2).Infof("Azure VirtualMachineScaleSetsClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure VirtualMachineScaleSetsClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azVirtualMachineScaleSetsClient{ client: virtualMachineScaleSetsClient, rateLimiterReader: rateLimiterReader, @@ -1082,10 +1064,10 @@ type azVirtualMachineScaleSetVMsClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzVirtualMachineScaleSetVMsClient(config *azClientConfig) *azVirtualMachineScaleSetVMsClient { - virtualMachineScaleSetVMsClient := compute.NewVirtualMachineScaleSetVMsClient(config.subscriptionID) - virtualMachineScaleSetVMsClient.BaseURI = config.resourceManagerEndpoint - virtualMachineScaleSetVMsClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzVirtualMachineScaleSetVMsClient(config *azclients.ClientConfig) *azVirtualMachineScaleSetVMsClient { + virtualMachineScaleSetVMsClient := compute.NewVirtualMachineScaleSetVMsClient(config.SubscriptionID) + virtualMachineScaleSetVMsClient.BaseURI = config.ResourceManagerEndpoint + virtualMachineScaleSetVMsClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) virtualMachineScaleSetVMsClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { virtualMachineScaleSetVMsClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -1094,12 +1076,12 @@ func newAzVirtualMachineScaleSetVMsClient(config *azClientConfig) *azVirtualMach configureUserAgent(&virtualMachineScaleSetVMsClient.Client) klog.V(2).Infof("Azure VirtualMachineScaleSetVMsClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure VirtualMachineScaleSetVMsClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azVirtualMachineScaleSetVMsClient{ client: virtualMachineScaleSetVMsClient, rateLimiterReader: rateLimiterReader, @@ -1185,10 +1167,10 @@ type azRoutesClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzRoutesClient(config *azClientConfig) *azRoutesClient { - routesClient := network.NewRoutesClient(config.subscriptionID) - routesClient.BaseURI = config.resourceManagerEndpoint - routesClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzRoutesClient(config *azclients.ClientConfig) *azRoutesClient { + routesClient := network.NewRoutesClient(config.SubscriptionID) + routesClient.BaseURI = config.ResourceManagerEndpoint + routesClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) routesClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { routesClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -1197,12 +1179,12 @@ func newAzRoutesClient(config *azClientConfig) *azRoutesClient { configureUserAgent(&routesClient.Client) klog.V(2).Infof("Azure RoutesClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure RoutesClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azRoutesClient{ client: routesClient, rateLimiterReader: rateLimiterReader, @@ -1295,10 +1277,10 @@ type azRouteTablesClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzRouteTablesClient(config *azClientConfig) *azRouteTablesClient { - routeTablesClient := network.NewRouteTablesClient(config.subscriptionID) - routeTablesClient.BaseURI = config.resourceManagerEndpoint - routeTablesClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzRouteTablesClient(config *azclients.ClientConfig) *azRouteTablesClient { + routeTablesClient := network.NewRouteTablesClient(config.SubscriptionID) + routeTablesClient.BaseURI = config.ResourceManagerEndpoint + routeTablesClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) routeTablesClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { routeTablesClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -1307,12 +1289,12 @@ func newAzRouteTablesClient(config *azClientConfig) *azRouteTablesClient { configureUserAgent(&routeTablesClient.Client) klog.V(2).Infof("Azure RouteTablesClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure RouteTablesClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azRouteTablesClient{ client: routeTablesClient, rateLimiterReader: rateLimiterReader, @@ -1400,9 +1382,9 @@ type azStorageAccountClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzStorageAccountClient(config *azClientConfig) *azStorageAccountClient { - storageAccountClient := storage.NewAccountsClientWithBaseURI(config.resourceManagerEndpoint, config.subscriptionID) - storageAccountClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzStorageAccountClient(config *azclients.ClientConfig) *azStorageAccountClient { + storageAccountClient := storage.NewAccountsClientWithBaseURI(config.ResourceManagerEndpoint, config.SubscriptionID) + storageAccountClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) storageAccountClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { storageAccountClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -1411,12 +1393,12 @@ func newAzStorageAccountClient(config *azClientConfig) *azStorageAccountClient { configureUserAgent(&storageAccountClient.Client) klog.V(2).Infof("Azure StorageAccountClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure StorageAccountClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azStorageAccountClient{ client: storageAccountClient, rateLimiterReader: rateLimiterReader, @@ -1528,9 +1510,9 @@ type azDisksClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzDisksClient(config *azClientConfig) *azDisksClient { - disksClient := compute.NewDisksClientWithBaseURI(config.resourceManagerEndpoint, config.subscriptionID) - disksClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzDisksClient(config *azclients.ClientConfig) *azDisksClient { + disksClient := compute.NewDisksClientWithBaseURI(config.ResourceManagerEndpoint, config.SubscriptionID) + disksClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) disksClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { disksClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -1539,12 +1521,12 @@ func newAzDisksClient(config *azClientConfig) *azDisksClient { configureUserAgent(&disksClient.Client) klog.V(2).Infof("Azure DisksClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure DisksClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azDisksClient{ client: disksClient, rateLimiterReader: rateLimiterReader, @@ -1615,9 +1597,9 @@ func (az *azDisksClient) Get(ctx context.Context, resourceGroupName string, disk } // TODO(feiskyer): refactor compute.SnapshotsClient to Interface. -func newSnapshotsClient(config *azClientConfig) *compute.SnapshotsClient { - snapshotsClient := compute.NewSnapshotsClientWithBaseURI(config.resourceManagerEndpoint, config.subscriptionID) - snapshotsClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newSnapshotsClient(config *azclients.ClientConfig) *compute.SnapshotsClient { + snapshotsClient := compute.NewSnapshotsClientWithBaseURI(config.ResourceManagerEndpoint, config.SubscriptionID) + snapshotsClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) snapshotsClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { snapshotsClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -1634,10 +1616,10 @@ type azVirtualMachineSizesClient struct { rateLimiterWriter flowcontrol.RateLimiter } -func newAzVirtualMachineSizesClient(config *azClientConfig) *azVirtualMachineSizesClient { - VirtualMachineSizesClient := compute.NewVirtualMachineSizesClient(config.subscriptionID) - VirtualMachineSizesClient.BaseURI = config.resourceManagerEndpoint - VirtualMachineSizesClient.Authorizer = autorest.NewBearerAuthorizer(config.servicePrincipalToken) +func newAzVirtualMachineSizesClient(config *azclients.ClientConfig) *azVirtualMachineSizesClient { + VirtualMachineSizesClient := compute.NewVirtualMachineSizesClient(config.SubscriptionID) + VirtualMachineSizesClient.BaseURI = config.ResourceManagerEndpoint + VirtualMachineSizesClient.Authorizer = autorest.NewBearerAuthorizer(config.ServicePrincipalToken) VirtualMachineSizesClient.PollingDelay = 5 * time.Second if config.ShouldOmitCloudProviderBackoff { VirtualMachineSizesClient.RetryAttempts = config.CloudProviderBackoffRetries @@ -1646,12 +1628,12 @@ func newAzVirtualMachineSizesClient(config *azClientConfig) *azVirtualMachineSiz configureUserAgent(&VirtualMachineSizesClient.Client) klog.V(2).Infof("Azure VirtualMachineSizesClient (read ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPS, - config.rateLimitConfig.CloudProviderRateLimitBucket) + config.RateLimitConfig.CloudProviderRateLimitQPS, + config.RateLimitConfig.CloudProviderRateLimitBucket) klog.V(2).Infof("Azure VirtualMachineSizesClient (write ops) using rate limit config: QPS=%g, bucket=%d", - config.rateLimitConfig.CloudProviderRateLimitQPSWrite, - config.rateLimitConfig.CloudProviderRateLimitBucketWrite) - rateLimiterReader, rateLimiterWriter := NewRateLimiter(config.rateLimitConfig) + config.RateLimitConfig.CloudProviderRateLimitQPSWrite, + config.RateLimitConfig.CloudProviderRateLimitBucketWrite) + rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) return &azVirtualMachineSizesClient{ rateLimiterReader: rateLimiterReader, rateLimiterWriter: rateLimiterWriter, diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/azure_ratelimit.go b/staging/src/k8s.io/legacy-cloud-providers/azure/azure_ratelimit.go index bae3dee59bc..12165717356 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/azure_ratelimit.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/azure_ratelimit.go @@ -19,42 +19,28 @@ limitations under the License. package azure import ( - "k8s.io/client-go/util/flowcontrol" + azclients "k8s.io/legacy-cloud-providers/azure/clients" ) -// RateLimitConfig indicates the rate limit config options. -type RateLimitConfig struct { - // Enable rate limiting - CloudProviderRateLimit bool `json:"cloudProviderRateLimit,omitempty" yaml:"cloudProviderRateLimit,omitempty"` - // Rate limit QPS (Read) - CloudProviderRateLimitQPS float32 `json:"cloudProviderRateLimitQPS,omitempty" yaml:"cloudProviderRateLimitQPS,omitempty"` - // Rate limit Bucket Size - CloudProviderRateLimitBucket int `json:"cloudProviderRateLimitBucket,omitempty" yaml:"cloudProviderRateLimitBucket,omitempty"` - // Rate limit QPS (Write) - CloudProviderRateLimitQPSWrite float32 `json:"cloudProviderRateLimitQPSWrite,omitempty" yaml:"cloudProviderRateLimitQPSWrite,omitempty"` - // Rate limit Bucket Size - CloudProviderRateLimitBucketWrite int `json:"cloudProviderRateLimitBucketWrite,omitempty" yaml:"cloudProviderRateLimitBucketWrite,omitempty"` -} - // CloudProviderRateLimitConfig indicates the rate limit config for each clients. type CloudProviderRateLimitConfig struct { // The default rate limit config options. - RateLimitConfig + azclients.RateLimitConfig // Rate limit config for each clients. Values would override default settings above. - RouteRateLimit *RateLimitConfig `json:"routeRateLimit,omitempty" yaml:"routeRateLimit,omitempty"` - SubnetsRateLimit *RateLimitConfig `json:"subnetsRateLimit,omitempty" yaml:"subnetsRateLimit,omitempty"` - InterfaceRateLimit *RateLimitConfig `json:"interfaceRateLimit,omitempty" yaml:"interfaceRateLimit,omitempty"` - RouteTableRateLimit *RateLimitConfig `json:"routeTableRateLimit,omitempty" yaml:"routeTableRateLimit,omitempty"` - LoadBalancerRateLimit *RateLimitConfig `json:"loadBalancerRateLimit,omitempty" yaml:"loadBalancerRateLimit,omitempty"` - PublicIPAddressRateLimit *RateLimitConfig `json:"publicIPAddressRateLimit,omitempty" yaml:"publicIPAddressRateLimit,omitempty"` - SecurityGroupRateLimit *RateLimitConfig `json:"securityGroupRateLimit,omitempty" yaml:"securityGroupRateLimit,omitempty"` - VirtualMachineRateLimit *RateLimitConfig `json:"virtualMachineRateLimit,omitempty" yaml:"virtualMachineRateLimit,omitempty"` - StorageAccountRateLimit *RateLimitConfig `json:"storageAccountRateLimit,omitempty" yaml:"storageAccountRateLimit,omitempty"` - DiskRateLimit *RateLimitConfig `json:"diskRateLimit,omitempty" yaml:"diskRateLimit,omitempty"` - SnapshotRateLimit *RateLimitConfig `json:"snapshotRateLimit,omitempty" yaml:"snapshotRateLimit,omitempty"` - VirtualMachineScaleSetRateLimit *RateLimitConfig `json:"virtualMachineScaleSetRateLimit,omitempty" yaml:"virtualMachineScaleSetRateLimit,omitempty"` - VirtualMachineSizeRateLimit *RateLimitConfig `json:"virtualMachineSizesRateLimit,omitempty" yaml:"virtualMachineSizesRateLimit,omitempty"` + RouteRateLimit *azclients.RateLimitConfig `json:"routeRateLimit,omitempty" yaml:"routeRateLimit,omitempty"` + SubnetsRateLimit *azclients.RateLimitConfig `json:"subnetsRateLimit,omitempty" yaml:"subnetsRateLimit,omitempty"` + InterfaceRateLimit *azclients.RateLimitConfig `json:"interfaceRateLimit,omitempty" yaml:"interfaceRateLimit,omitempty"` + RouteTableRateLimit *azclients.RateLimitConfig `json:"routeTableRateLimit,omitempty" yaml:"routeTableRateLimit,omitempty"` + LoadBalancerRateLimit *azclients.RateLimitConfig `json:"loadBalancerRateLimit,omitempty" yaml:"loadBalancerRateLimit,omitempty"` + PublicIPAddressRateLimit *azclients.RateLimitConfig `json:"publicIPAddressRateLimit,omitempty" yaml:"publicIPAddressRateLimit,omitempty"` + SecurityGroupRateLimit *azclients.RateLimitConfig `json:"securityGroupRateLimit,omitempty" yaml:"securityGroupRateLimit,omitempty"` + VirtualMachineRateLimit *azclients.RateLimitConfig `json:"virtualMachineRateLimit,omitempty" yaml:"virtualMachineRateLimit,omitempty"` + StorageAccountRateLimit *azclients.RateLimitConfig `json:"storageAccountRateLimit,omitempty" yaml:"storageAccountRateLimit,omitempty"` + DiskRateLimit *azclients.RateLimitConfig `json:"diskRateLimit,omitempty" yaml:"diskRateLimit,omitempty"` + SnapshotRateLimit *azclients.RateLimitConfig `json:"snapshotRateLimit,omitempty" yaml:"snapshotRateLimit,omitempty"` + VirtualMachineScaleSetRateLimit *azclients.RateLimitConfig `json:"virtualMachineScaleSetRateLimit,omitempty" yaml:"virtualMachineScaleSetRateLimit,omitempty"` + VirtualMachineSizeRateLimit *azclients.RateLimitConfig `json:"virtualMachineSizesRateLimit,omitempty" yaml:"virtualMachineSizesRateLimit,omitempty"` } // InitializeCloudProviderRateLimitConfig initializes rate limit configs. @@ -94,7 +80,7 @@ func InitializeCloudProviderRateLimitConfig(config *CloudProviderRateLimitConfig } // overrideDefaultRateLimitConfig overrides the default CloudProviderRateLimitConfig. -func overrideDefaultRateLimitConfig(defaults, config *RateLimitConfig) *RateLimitConfig { +func overrideDefaultRateLimitConfig(defaults, config *azclients.RateLimitConfig) *azclients.RateLimitConfig { // If config not set, apply defaults. if config == nil { return defaults @@ -102,7 +88,7 @@ func overrideDefaultRateLimitConfig(defaults, config *RateLimitConfig) *RateLimi // Remain disabled if it's set explicitly. if !config.CloudProviderRateLimit { - return &RateLimitConfig{CloudProviderRateLimit: false} + return &azclients.RateLimitConfig{CloudProviderRateLimit: false} } // Apply default values. @@ -121,26 +107,3 @@ func overrideDefaultRateLimitConfig(defaults, config *RateLimitConfig) *RateLimi return config } - -// RateLimitEnabled returns true if CloudProviderRateLimit is set to true. -func RateLimitEnabled(config *RateLimitConfig) bool { - return config != nil && config.CloudProviderRateLimit -} - -// NewRateLimiter creates new read and write flowcontrol.RateLimiter from RateLimitConfig. -func NewRateLimiter(config *RateLimitConfig) (flowcontrol.RateLimiter, flowcontrol.RateLimiter) { - readLimiter := flowcontrol.NewFakeAlwaysRateLimiter() - writeLimiter := flowcontrol.NewFakeAlwaysRateLimiter() - - if config != nil && config.CloudProviderRateLimit { - readLimiter = flowcontrol.NewTokenBucketRateLimiter( - config.CloudProviderRateLimitQPS, - config.CloudProviderRateLimitBucket) - - writeLimiter = flowcontrol.NewTokenBucketRateLimiter( - config.CloudProviderRateLimitQPSWrite, - config.CloudProviderRateLimitBucketWrite) - } - - return readLimiter, writeLimiter -} diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/azure_ratelimit_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/azure_ratelimit_test.go index 9601d45f4e6..510bcba826e 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/azure_ratelimit_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/azure_ratelimit_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/assert" "k8s.io/legacy-cloud-providers/azure/auth" + azclients "k8s.io/legacy-cloud-providers/azure/clients" ) var ( @@ -78,7 +79,7 @@ var ( vmType: "standard" }` - testDefaultRateLimitConfig = RateLimitConfig{ + testDefaultRateLimitConfig = azclients.RateLimitConfig{ CloudProviderRateLimit: true, CloudProviderRateLimitBucket: 1, CloudProviderRateLimitBucketWrite: 1, @@ -106,10 +107,10 @@ func TestParseConfig(t *testing.T) { CloudProviderBackoffRetries: 1, CloudProviderRateLimitConfig: CloudProviderRateLimitConfig{ RateLimitConfig: testDefaultRateLimitConfig, - LoadBalancerRateLimit: &RateLimitConfig{ + LoadBalancerRateLimit: &azclients.RateLimitConfig{ CloudProviderRateLimit: false, }, - VirtualMachineScaleSetRateLimit: &RateLimitConfig{ + VirtualMachineScaleSetRateLimit: &azclients.RateLimitConfig{ CloudProviderRateLimit: true, CloudProviderRateLimitBucket: 2, CloudProviderRateLimitBucketWrite: 2, @@ -149,10 +150,10 @@ func TestInitializeCloudProviderRateLimitConfig(t *testing.T) { assert.NoError(t, err) InitializeCloudProviderRateLimitConfig(&config.CloudProviderRateLimitConfig) - assert.Equal(t, config.LoadBalancerRateLimit, &RateLimitConfig{ + assert.Equal(t, config.LoadBalancerRateLimit, &azclients.RateLimitConfig{ CloudProviderRateLimit: false, }) - assert.Equal(t, config.VirtualMachineScaleSetRateLimit, &RateLimitConfig{ + assert.Equal(t, config.VirtualMachineScaleSetRateLimit, &azclients.RateLimitConfig{ CloudProviderRateLimit: true, CloudProviderRateLimitBucket: 2, CloudProviderRateLimitBucketWrite: 2, diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/BUILD b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/BUILD new file mode 100644 index 00000000000..7d742947cb1 --- /dev/null +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/BUILD @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "azure_client_config.go", + "doc.go", + ], + importmap = "k8s.io/kubernetes/vendor/k8s.io/legacy-cloud-providers/azure/clients", + importpath = "k8s.io/legacy-cloud-providers/azure/clients", + visibility = ["//visibility:public"], + deps = [ + "//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library", + "//staging/src/k8s.io/legacy-cloud-providers/azure/retry:go_default_library", + "//vendor/github.com/Azure/go-autorest/autorest/adal:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = ["azure_client_config_test.go"], + embed = [":go_default_library"], + deps = [ + "//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library", + "//vendor/github.com/stretchr/testify/assert:go_default_library", + ], +) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/azure_client_config.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/azure_client_config.go new file mode 100644 index 00000000000..88aef868e00 --- /dev/null +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/azure_client_config.go @@ -0,0 +1,84 @@ +// +build !providerless + +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package clients + +import ( + "github.com/Azure/go-autorest/autorest/adal" + "k8s.io/client-go/util/flowcontrol" + "k8s.io/legacy-cloud-providers/azure/retry" +) + +// ClientConfig contains all essential information to create an Azure client. +type ClientConfig struct { + Location string + SubscriptionID string + ResourceManagerEndpoint string + ServicePrincipalToken *adal.ServicePrincipalToken + RateLimitConfig *RateLimitConfig + Backoff *retry.Backoff + + // Depracated configures (retry.Backoff is preferred). + // Those configurations would be removed after all Azure clients are moved to new implementations. + CloudProviderBackoffRetries int + CloudProviderBackoffDuration int + ShouldOmitCloudProviderBackoff bool +} + +// WithRateLimiter returns ClientConfig with rateLimitConfig set. +func (cfg *ClientConfig) WithRateLimiter(rl *RateLimitConfig) *ClientConfig { + cfg.RateLimitConfig = rl + return cfg +} + +// RateLimitConfig indicates the rate limit config options. +type RateLimitConfig struct { + // Enable rate limiting + CloudProviderRateLimit bool `json:"cloudProviderRateLimit,omitempty" yaml:"cloudProviderRateLimit,omitempty"` + // Rate limit QPS (Read) + CloudProviderRateLimitQPS float32 `json:"cloudProviderRateLimitQPS,omitempty" yaml:"cloudProviderRateLimitQPS,omitempty"` + // Rate limit Bucket Size + CloudProviderRateLimitBucket int `json:"cloudProviderRateLimitBucket,omitempty" yaml:"cloudProviderRateLimitBucket,omitempty"` + // Rate limit QPS (Write) + CloudProviderRateLimitQPSWrite float32 `json:"cloudProviderRateLimitQPSWrite,omitempty" yaml:"cloudProviderRateLimitQPSWrite,omitempty"` + // Rate limit Bucket Size + CloudProviderRateLimitBucketWrite int `json:"cloudProviderRateLimitBucketWrite,omitempty" yaml:"cloudProviderRateLimitBucketWrite,omitempty"` +} + +// RateLimitEnabled returns true if CloudProviderRateLimit is set to true. +func RateLimitEnabled(config *RateLimitConfig) bool { + return config != nil && config.CloudProviderRateLimit +} + +// NewRateLimiter creates new read and write flowcontrol.RateLimiter from RateLimitConfig. +func NewRateLimiter(config *RateLimitConfig) (flowcontrol.RateLimiter, flowcontrol.RateLimiter) { + readLimiter := flowcontrol.NewFakeAlwaysRateLimiter() + writeLimiter := flowcontrol.NewFakeAlwaysRateLimiter() + + if config != nil && config.CloudProviderRateLimit { + readLimiter = flowcontrol.NewTokenBucketRateLimiter( + config.CloudProviderRateLimitQPS, + config.CloudProviderRateLimitBucket) + + writeLimiter = flowcontrol.NewTokenBucketRateLimiter( + config.CloudProviderRateLimitQPSWrite, + config.CloudProviderRateLimitBucketWrite) + } + + return readLimiter, writeLimiter +} diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/azure_client_config_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/azure_client_config_test.go new file mode 100644 index 00000000000..f06db05d988 --- /dev/null +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/azure_client_config_test.go @@ -0,0 +1,68 @@ +// +build !providerless + +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package clients + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/client-go/util/flowcontrol" +) + +func TestWithRateLimiter(t *testing.T) { + config := &ClientConfig{} + assert.Nil(t, config.RateLimitConfig) + config.WithRateLimiter(&RateLimitConfig{CloudProviderRateLimit: true}) + assert.Equal(t, &RateLimitConfig{CloudProviderRateLimit: true}, config.RateLimitConfig) + config.WithRateLimiter(nil) + assert.Nil(t, config.RateLimitConfig) +} + +func TestRateLimitEnabled(t *testing.T) { + assert.Equal(t, false, RateLimitEnabled(nil)) + config := &RateLimitConfig{} + assert.Equal(t, false, RateLimitEnabled(config)) + config.CloudProviderRateLimit = true + assert.Equal(t, true, RateLimitEnabled(config)) +} + +func TestNewRateLimiter(t *testing.T) { + fakeRateLimiter := flowcontrol.NewFakeAlwaysRateLimiter() + readLimiter, writeLimiter := NewRateLimiter(nil) + assert.Equal(t, readLimiter, fakeRateLimiter) + assert.Equal(t, writeLimiter, fakeRateLimiter) + + rateLimitConfig := &RateLimitConfig{ + CloudProviderRateLimit: false, + } + readLimiter, writeLimiter = NewRateLimiter(rateLimitConfig) + assert.Equal(t, readLimiter, fakeRateLimiter) + assert.Equal(t, writeLimiter, fakeRateLimiter) + + rateLimitConfig = &RateLimitConfig{ + CloudProviderRateLimit: true, + CloudProviderRateLimitQPS: 3, + CloudProviderRateLimitBucket: 10, + CloudProviderRateLimitQPSWrite: 1, + CloudProviderRateLimitBucketWrite: 3, + } + readLimiter, writeLimiter = NewRateLimiter(rateLimitConfig) + assert.Equal(t, flowcontrol.NewTokenBucketRateLimiter(3, 10), readLimiter) + assert.Equal(t, flowcontrol.NewTokenBucketRateLimiter(1, 3), writeLimiter) +} diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/doc.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/doc.go new file mode 100644 index 00000000000..dbfc5f613f4 --- /dev/null +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/doc.go @@ -0,0 +1,20 @@ +// +build !providerless + +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package clients contains a set of Azure ARM clients. +package clients // import "k8s.io/legacy-cloud-providers/azure/clients" diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/doc.go b/staging/src/k8s.io/legacy-cloud-providers/azure/doc.go index 55dd87189f0..a7e12217741 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/doc.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/doc.go @@ -1,3 +1,5 @@ +// +build !providerless + /* Copyright 2019 The Kubernetes Authors. @@ -14,4 +16,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -package azure +// Package azure is an implementation of CloudProvider Interface, LoadBalancer +// and Instances for Azure. +package azure // import "k8s.io/legacy-cloud-providers/azure" diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/retry/BUILD b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/BUILD index e3ecf81dfb1..6db50eb4594 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/retry/BUILD +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/BUILD @@ -2,18 +2,32 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = ["azure_error.go"], + srcs = [ + "azure_error.go", + "azure_retry.go", + "doc.go", + ], importmap = "k8s.io/kubernetes/vendor/k8s.io/legacy-cloud-providers/azure/retry", importpath = "k8s.io/legacy-cloud-providers/azure/retry", visibility = ["//visibility:public"], - deps = ["//vendor/k8s.io/klog:go_default_library"], + deps = [ + "//vendor/github.com/Azure/go-autorest/autorest:go_default_library", + "//vendor/github.com/Azure/go-autorest/autorest/mocks:go_default_library", + "//vendor/k8s.io/klog:go_default_library", + ], ) go_test( name = "go_default_test", - srcs = ["azure_error_test.go"], + srcs = [ + "azure_error_test.go", + "azure_retry_test.go", + ], embed = [":go_default_library"], - deps = ["//vendor/github.com/stretchr/testify/assert:go_default_library"], + deps = [ + "//vendor/github.com/Azure/go-autorest/autorest/mocks:go_default_library", + "//vendor/github.com/stretchr/testify/assert:go_default_library", + ], ) filegroup( diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_error.go b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_error.go index 4bf262897dc..9e604e973de 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_error.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_error.go @@ -19,7 +19,9 @@ limitations under the License. package retry import ( + "bytes" "fmt" + "io/ioutil" "net/http" "strconv" "strings" @@ -28,6 +30,11 @@ import ( "k8s.io/klog" ) +const ( + // RetryAfterHeaderKey is the retry-after header key in ARM responses. + RetryAfterHeaderKey = "Retry-After" +) + var ( // The function to get current time. now = time.Now @@ -57,6 +64,15 @@ func (err *Error) Error() error { err.Retriable, err.RetryAfter.String(), err.HTTPStatusCode, err.RawError) } +// IsThrottled returns true the if the request is being throttled. +func (err *Error) IsThrottled() bool { + if err == nil { + return false + } + + return err.HTTPStatusCode == http.StatusTooManyRequests || err.RetryAfter.After(now()) +} + // NewError creates a new Error. func NewError(retriable bool, err error) *Error { return &Error{ @@ -73,6 +89,20 @@ func GetRetriableError(err error) *Error { } } +// GetRateLimitError creates a new error for rate limiting. +func GetRateLimitError(isWrite bool, opName string) *Error { + opType := "read" + if isWrite { + opType = "write" + } + return GetRetriableError(fmt.Errorf("azure cloud provider rate limited(%s) for operation %q", opType, opName)) +} + +// GetThrottlingError creates a new error for throttling. +func GetThrottlingError(operation, reason string) *Error { + return GetRetriableError(fmt.Errorf("azure cloud provider throttled for operation %s with reason %q", operation, reason)) +} + // GetError gets a new Error based on resp and error. func GetError(resp *http.Response, err error) *Error { if err == nil && resp == nil { @@ -88,12 +118,8 @@ func GetError(resp *http.Response, err error) *Error { if retryAfterDuration := getRetryAfter(resp); retryAfterDuration != 0 { retryAfter = now().Add(retryAfterDuration) } - rawError := err - if err == nil && resp != nil { - rawError = fmt.Errorf("HTTP response: %v", resp.StatusCode) - } return &Error{ - RawError: rawError, + RawError: getRawError(resp, err), RetryAfter: retryAfter, Retriable: shouldRetryHTTPRequest(resp, err), HTTPStatusCode: getHTTPStatusCode(resp), @@ -114,6 +140,27 @@ func isSuccessHTTPResponse(resp *http.Response) bool { return false } +func getRawError(resp *http.Response, err error) error { + if err != nil { + return err + } + + if resp == nil || resp.Body == nil { + return fmt.Errorf("empty HTTP response") + } + + // return the http status if unabled to get response body. + defer resp.Body.Close() + respBody, _ := ioutil.ReadAll(resp.Body) + resp.Body = ioutil.NopCloser(bytes.NewReader(respBody)) + if len(respBody) == 0 { + return fmt.Errorf("HTTP status code (%d)", resp.StatusCode) + } + + // return the raw response body. + return fmt.Errorf("%s", string(respBody)) +} + func getHTTPStatusCode(resp *http.Response) int { if resp == nil { return -1 @@ -151,7 +198,7 @@ func getRetryAfter(resp *http.Response) time.Duration { return 0 } - ra := resp.Header.Get("Retry-After") + ra := resp.Header.Get(RetryAfterHeaderKey) if ra == "" { return 0 } diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_error_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_error_test.go index 52057ffc3b4..4eafe565b17 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_error_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_error_test.go @@ -19,7 +19,9 @@ limitations under the License. package retry import ( + "bytes" "fmt" + "io/ioutil" "net/http" "testing" "time" @@ -44,11 +46,11 @@ func TestGetError(t *testing.T) { }, { code: http.StatusOK, - err: fmt.Errorf("some error"), + err: fmt.Errorf("unknown error"), expected: &Error{ Retriable: true, HTTPStatusCode: http.StatusOK, - RawError: fmt.Errorf("some error"), + RawError: fmt.Errorf("unknown error"), }, }, { @@ -56,7 +58,7 @@ func TestGetError(t *testing.T) { expected: &Error{ Retriable: false, HTTPStatusCode: http.StatusBadRequest, - RawError: fmt.Errorf("HTTP response: 400"), + RawError: fmt.Errorf("some error"), }, }, { @@ -64,7 +66,7 @@ func TestGetError(t *testing.T) { expected: &Error{ Retriable: true, HTTPStatusCode: http.StatusInternalServerError, - RawError: fmt.Errorf("HTTP response: 500"), + RawError: fmt.Errorf("some error"), }, }, { @@ -83,7 +85,7 @@ func TestGetError(t *testing.T) { Retriable: true, HTTPStatusCode: http.StatusTooManyRequests, RetryAfter: now().Add(100 * time.Second), - RawError: fmt.Errorf("HTTP response: 429"), + RawError: fmt.Errorf("some error"), }, }, } @@ -92,6 +94,7 @@ func TestGetError(t *testing.T) { resp := &http.Response{ StatusCode: test.code, Header: http.Header{}, + Body: ioutil.NopCloser(bytes.NewReader([]byte("some error"))), } if test.retryAfter != 0 { resp.Header.Add("Retry-After", fmt.Sprintf("%d", test.retryAfter)) @@ -138,7 +141,7 @@ func TestGetStatusNotFoundAndForbiddenIgnoredError(t *testing.T) { expected: &Error{ Retriable: false, HTTPStatusCode: http.StatusBadRequest, - RawError: fmt.Errorf("HTTP response: 400"), + RawError: fmt.Errorf("some error"), }, }, { @@ -146,7 +149,7 @@ func TestGetStatusNotFoundAndForbiddenIgnoredError(t *testing.T) { expected: &Error{ Retriable: true, HTTPStatusCode: http.StatusInternalServerError, - RawError: fmt.Errorf("HTTP response: 500"), + RawError: fmt.Errorf("some error"), }, }, { @@ -165,7 +168,7 @@ func TestGetStatusNotFoundAndForbiddenIgnoredError(t *testing.T) { Retriable: true, HTTPStatusCode: http.StatusTooManyRequests, RetryAfter: now().Add(100 * time.Second), - RawError: fmt.Errorf("HTTP response: 429"), + RawError: fmt.Errorf("some error"), }, }, } @@ -174,6 +177,7 @@ func TestGetStatusNotFoundAndForbiddenIgnoredError(t *testing.T) { resp := &http.Response{ StatusCode: test.code, Header: http.Header{}, + Body: ioutil.NopCloser(bytes.NewReader([]byte("some error"))), } if test.retryAfter != 0 { resp.Header.Add("Retry-After", fmt.Sprintf("%d", test.retryAfter)) @@ -251,3 +255,38 @@ func TestIsSuccessResponse(t *testing.T) { } } } + +func TestIsThrottled(t *testing.T) { + tests := []struct { + err *Error + expected bool + }{ + { + err: nil, + expected: false, + }, + { + err: &Error{ + HTTPStatusCode: http.StatusOK, + }, + expected: false, + }, + { + err: &Error{ + HTTPStatusCode: http.StatusTooManyRequests, + }, + expected: true, + }, + { + err: &Error{ + RetryAfter: time.Now().Add(time.Hour), + }, + expected: true, + }, + } + + for _, test := range tests { + real := test.err.IsThrottled() + assert.Equal(t, test.expected, real) + } +} diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_retry.go b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_retry.go new file mode 100644 index 00000000000..bc8a4778541 --- /dev/null +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_retry.go @@ -0,0 +1,165 @@ +// +build !providerless + +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package retry + +import ( + "math/rand" + "net/http" + "time" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/mocks" + "k8s.io/klog" +) + +// Ensure package autorest/mocks is imported and vendored. +var _ autorest.Sender = mocks.NewSender() + +// Backoff holds parameters applied to a Backoff function. +type Backoff struct { + // The initial duration. + Duration time.Duration + // Duration is multiplied by factor each iteration, if factor is not zero + // and the limits imposed by Steps and Cap have not been reached. + // Should not be negative. + // The jitter does not contribute to the updates to the duration parameter. + Factor float64 + // The sleep at each iteration is the duration plus an additional + // amount chosen uniformly at random from the interval between + // zero and `jitter*duration`. + Jitter float64 + // The remaining number of iterations in which the duration + // parameter may change (but progress can be stopped earlier by + // hitting the cap). If not positive, the duration is not + // changed. Used for exponential backoff in combination with + // Factor and Cap. + Steps int + // A limit on revised values of the duration parameter. If a + // multiplication by the factor parameter would make the duration + // exceed the cap then the duration is set to the cap and the + // steps parameter is set to zero. + Cap time.Duration +} + +// NewBackoff creates a new Backoff. +func NewBackoff(duration time.Duration, factor float64, jitter float64, steps int, cap time.Duration) *Backoff { + return &Backoff{ + Duration: duration, + Factor: factor, + Jitter: jitter, + Steps: steps, + Cap: cap, + } +} + +// Step (1) returns an amount of time to sleep determined by the +// original Duration and Jitter and (2) mutates the provided Backoff +// to update its Steps and Duration. +func (b *Backoff) Step() time.Duration { + if b.Steps < 1 { + if b.Jitter > 0 { + return jitter(b.Duration, b.Jitter) + } + return b.Duration + } + b.Steps-- + + duration := b.Duration + + // calculate the next step + if b.Factor != 0 { + b.Duration = time.Duration(float64(b.Duration) * b.Factor) + if b.Cap > 0 && b.Duration > b.Cap { + b.Duration = b.Cap + b.Steps = 0 + } + } + + if b.Jitter > 0 { + duration = jitter(duration, b.Jitter) + } + return duration +} + +// Jitter returns a time.Duration between duration and duration + maxFactor * +// duration. +// +// This allows clients to avoid converging on periodic behavior. If maxFactor +// is 0.0, a suggested default value will be chosen. +func jitter(duration time.Duration, maxFactor float64) time.Duration { + if maxFactor <= 0.0 { + maxFactor = 1.0 + } + wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration)) + return wait +} + +// DoExponentialBackoffRetry reprents an autorest.SendDecorator with backoff retry. +func DoExponentialBackoffRetry(backoff *Backoff) autorest.SendDecorator { + return func(s autorest.Sender) autorest.Sender { + return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { + return doBackoffRetry(s, r, backoff) + }) + } +} + +// doBackoffRetry does the backoff retries for the request. +func doBackoffRetry(s autorest.Sender, r *http.Request, backoff *Backoff) (resp *http.Response, err error) { + rr := autorest.NewRetriableRequest(r) + // Increment to add the first call (attempts denotes number of retries) + for backoff.Steps > 0 { + err = rr.Prepare() + if err != nil { + return + } + resp, err = s.Do(rr.Request()) + rerr := GetError(resp, err) + // Abort retries in the following scenarios: + // 1) request succeed + // 2) request is not retriable + // 3) request has been throttled + // 4) request has completed all the retry steps + if rerr == nil || !rerr.Retriable || rerr.IsThrottled() || backoff.Steps == 1 { + return resp, rerr.Error() + } + + if !delayForBackOff(backoff, r.Context().Done()) { + if r.Context().Err() != nil { + return resp, r.Context().Err() + } + return resp, rerr.Error() + } + + klog.V(3).Infof("Backoff retrying %s %q with error %v", r.Method, r.URL.String(), rerr) + } + + return resp, err +} + +// delayForBackOff invokes time.After for the supplied backoff duration. +// The delay may be canceled by closing the passed channel. If terminated early, returns false. +func delayForBackOff(backoff *Backoff, cancel <-chan struct{}) bool { + d := backoff.Step() + select { + case <-time.After(d): + return true + case <-cancel: + return false + } +} diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_retry_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_retry_test.go new file mode 100644 index 00000000000..8799792a96f --- /dev/null +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/azure_retry_test.go @@ -0,0 +1,125 @@ +// +build !providerless + +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package retry + +import ( + "fmt" + "math/rand" + "net/http" + "net/url" + "testing" + "time" + + "github.com/Azure/go-autorest/autorest/mocks" + "github.com/stretchr/testify/assert" +) + +func TestStep(t *testing.T) { + tests := []struct { + initial *Backoff + want []time.Duration + }{ + {initial: &Backoff{Duration: time.Second, Steps: 0}, want: []time.Duration{time.Second, time.Second, time.Second}}, + {initial: &Backoff{Duration: time.Second, Steps: 1}, want: []time.Duration{time.Second, time.Second, time.Second}}, + {initial: &Backoff{Duration: time.Second, Factor: 1.0, Steps: 1}, want: []time.Duration{time.Second, time.Second, time.Second}}, + {initial: &Backoff{Duration: time.Second, Factor: 2, Steps: 3}, want: []time.Duration{1 * time.Second, 2 * time.Second, 4 * time.Second}}, + {initial: &Backoff{Duration: time.Second, Factor: 2, Steps: 3, Cap: 3 * time.Second}, want: []time.Duration{1 * time.Second, 2 * time.Second, 3 * time.Second}}, + {initial: &Backoff{Duration: time.Second, Factor: 2, Steps: 2, Cap: 3 * time.Second, Jitter: 0.5}, want: []time.Duration{2 * time.Second, 3 * time.Second, 3 * time.Second}}, + {initial: &Backoff{Duration: time.Second, Factor: 2, Steps: 6, Jitter: 4}, want: []time.Duration{1 * time.Second, 2 * time.Second, 4 * time.Second, 8 * time.Second, 16 * time.Second, 32 * time.Second}}, + } + for seed := int64(0); seed < 5; seed++ { + for _, tt := range tests { + initial := *tt.initial + t.Run(fmt.Sprintf("%#v seed=%d", initial, seed), func(t *testing.T) { + rand.Seed(seed) + for i := 0; i < len(tt.want); i++ { + got := initial.Step() + t.Logf("[%d]=%s", i, got) + if initial.Jitter > 0 { + if got == tt.want[i] { + // this is statistically unlikely to happen by chance + t.Errorf("Backoff.Step(%d) = %v, no jitter", i, got) + continue + } + diff := float64(tt.want[i]-got) / float64(tt.want[i]) + if diff > initial.Jitter { + t.Errorf("Backoff.Step(%d) = %v, want %v, outside range", i, got, tt.want) + continue + } + } else { + if got != tt.want[i] { + t.Errorf("Backoff.Step(%d) = %v, want %v", i, got, tt.want) + continue + } + } + } + }) + } + } +} + +func TestDoBackoffRetry(t *testing.T) { + backoff := &Backoff{Factor: 1.0, Steps: 3} + fakeRequest := &http.Request{ + URL: &url.URL{ + Host: "localhost", + Path: "/api", + }, + } + r := mocks.NewResponseWithStatus("500 InternelServerError", http.StatusInternalServerError) + client := mocks.NewSender() + client.AppendAndRepeatResponse(r, 3) + + // retries up to steps on errors + expectedErr := &Error{ + Retriable: true, + HTTPStatusCode: 500, + RawError: fmt.Errorf("HTTP status code (500)"), + } + resp, err := doBackoffRetry(client, fakeRequest, backoff) + assert.NotNil(t, resp) + assert.Equal(t, 500, resp.StatusCode) + assert.Equal(t, expectedErr.Error(), err) + assert.Equal(t, 3, client.Attempts()) + + // returns immediately on succeed + r = mocks.NewResponseWithStatus("200 OK", http.StatusOK) + client = mocks.NewSender() + client.AppendAndRepeatResponse(r, 1) + resp, err = doBackoffRetry(client, fakeRequest, backoff) + assert.Nil(t, err) + assert.Equal(t, 1, client.Attempts()) + assert.NotNil(t, resp) + assert.Equal(t, 200, resp.StatusCode) + + // returns immediately on throttling + r = mocks.NewResponseWithStatus("429 TooManyRequests", http.StatusTooManyRequests) + client = mocks.NewSender() + client.AppendAndRepeatResponse(r, 1) + expectedErr = &Error{ + Retriable: true, + HTTPStatusCode: 429, + RawError: fmt.Errorf("HTTP status code (429)"), + } + resp, err = doBackoffRetry(client, fakeRequest, backoff) + assert.Equal(t, expectedErr.Error(), err) + assert.Equal(t, 1, client.Attempts()) + assert.NotNil(t, resp) + assert.Equal(t, 429, resp.StatusCode) +} diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/retry/doc.go b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/doc.go new file mode 100644 index 00000000000..a6c0fc930a6 --- /dev/null +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/retry/doc.go @@ -0,0 +1,21 @@ +// +build !providerless + +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package retry defines a general library to handle errors and retries for various +// Azure clients. +package retry // import "k8s.io/legacy-cloud-providers/azure/retry" diff --git a/staging/src/k8s.io/legacy-cloud-providers/go.mod b/staging/src/k8s.io/legacy-cloud-providers/go.mod index 96001956b82..bcf8585b806 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/go.mod +++ b/staging/src/k8s.io/legacy-cloud-providers/go.mod @@ -9,6 +9,7 @@ require ( github.com/Azure/azure-sdk-for-go v35.0.0+incompatible github.com/Azure/go-autorest/autorest v0.9.0 github.com/Azure/go-autorest/autorest/adal v0.5.0 + github.com/Azure/go-autorest/autorest/mocks v0.2.0 github.com/Azure/go-autorest/autorest/to v0.2.0 github.com/Azure/go-autorest/autorest/validation v0.1.0 // indirect github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534 diff --git a/staging/src/k8s.io/legacy-cloud-providers/go.sum b/staging/src/k8s.io/legacy-cloud-providers/go.sum index fb3a8acd010..1549e22a94c 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/go.sum +++ b/staging/src/k8s.io/legacy-cloud-providers/go.sum @@ -98,6 +98,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/vendor/github.com/Azure/go-autorest/autorest/BUILD b/vendor/github.com/Azure/go-autorest/autorest/BUILD index 97e7bf11079..299c517d93a 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/BUILD +++ b/vendor/github.com/Azure/go-autorest/autorest/BUILD @@ -40,6 +40,7 @@ filegroup( "//vendor/github.com/Azure/go-autorest/autorest/adal:all-srcs", "//vendor/github.com/Azure/go-autorest/autorest/azure:all-srcs", "//vendor/github.com/Azure/go-autorest/autorest/date:all-srcs", + "//vendor/github.com/Azure/go-autorest/autorest/mocks:all-srcs", "//vendor/github.com/Azure/go-autorest/autorest/to:all-srcs", "//vendor/github.com/Azure/go-autorest/autorest/validation:all-srcs", ], diff --git a/vendor/github.com/Azure/go-autorest/autorest/mocks/BUILD b/vendor/github.com/Azure/go-autorest/autorest/mocks/BUILD new file mode 100644 index 00000000000..701650a9313 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/mocks/BUILD @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "helpers.go", + "mocks.go", + ], + importmap = "k8s.io/kubernetes/vendor/github.com/Azure/go-autorest/autorest/mocks", + importpath = "github.com/Azure/go-autorest/autorest/mocks", + visibility = ["//visibility:public"], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/vendor/github.com/Azure/go-autorest/autorest/mocks/LICENSE b/vendor/github.com/Azure/go-autorest/autorest/mocks/LICENSE new file mode 100644 index 00000000000..b9d6a27ea92 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/mocks/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015 Microsoft Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/Azure/go-autorest/autorest/mocks/go.mod b/vendor/github.com/Azure/go-autorest/autorest/mocks/go.mod new file mode 100644 index 00000000000..4d726a44e4b --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/mocks/go.mod @@ -0,0 +1,3 @@ +module github.com/Azure/go-autorest/autorest/mocks + +go 1.12 diff --git a/vendor/github.com/Azure/go-autorest/autorest/mocks/helpers.go b/vendor/github.com/Azure/go-autorest/autorest/mocks/helpers.go new file mode 100644 index 00000000000..f8b2f8b1ab6 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/mocks/helpers.go @@ -0,0 +1,171 @@ +package mocks + +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import ( + "fmt" + "io" + "net/http" + "time" +) + +const ( + // TestAuthorizationHeader is a faux HTTP Authorization header value + TestAuthorizationHeader = "BEARER SECRETTOKEN" + + // TestBadURL is a malformed URL + TestBadURL = " " + + // TestDelay is the Retry-After delay used in tests. + TestDelay = 0 * time.Second + + // TestHeader is the header used in tests. + TestHeader = "x-test-header" + + // TestURL is the URL used in tests. + TestURL = "https://microsoft.com/a/b/c/" + + // TestAzureAsyncURL is a URL used in Azure asynchronous tests + TestAzureAsyncURL = "https://microsoft.com/a/b/c/async" + + // TestLocationURL is a URL used in Azure asynchronous tests + TestLocationURL = "https://microsoft.com/a/b/c/location" +) + +const ( + headerLocation = "Location" + headerRetryAfter = "Retry-After" +) + +// NewRequest instantiates a new request. +func NewRequest() *http.Request { + return NewRequestWithContent("") +} + +// NewRequestWithContent instantiates a new request using the passed string for the body content. +func NewRequestWithContent(c string) *http.Request { + r, _ := http.NewRequest("GET", "https://microsoft.com/a/b/c/", NewBody(c)) + return r +} + +// NewRequestWithCloseBody instantiates a new request. +func NewRequestWithCloseBody() *http.Request { + return NewRequestWithCloseBodyContent("request body") +} + +// NewRequestWithCloseBodyContent instantiates a new request using the passed string for the body content. +func NewRequestWithCloseBodyContent(c string) *http.Request { + r, _ := http.NewRequest("GET", "https://microsoft.com/a/b/c/", NewBodyClose(c)) + return r +} + +// NewRequestForURL instantiates a new request using the passed URL. +func NewRequestForURL(u string) *http.Request { + return NewRequestWithParams("GET", u, NewBody("")) +} + +// NewRequestWithParams instantiates a new request using the provided parameters. +func NewRequestWithParams(method, u string, body io.Reader) *http.Request { + r, err := http.NewRequest(method, u, body) + if err != nil { + panic(fmt.Sprintf("mocks: ERROR (%v) parsing testing URL %s", err, u)) + } + return r +} + +// NewResponse instantiates a new response. +func NewResponse() *http.Response { + return NewResponseWithContent("") +} + +// NewResponseWithBytes instantiates a new response with the passed bytes as the body content. +func NewResponseWithBytes(input []byte) *http.Response { + return &http.Response{ + Status: "200 OK", + StatusCode: 200, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Body: NewBodyWithBytes(input), + Request: NewRequest(), + } +} + +// NewResponseWithContent instantiates a new response with the passed string as the body content. +func NewResponseWithContent(c string) *http.Response { + return &http.Response{ + Status: "200 OK", + StatusCode: 200, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Body: NewBody(c), + Request: NewRequest(), + } +} + +// NewResponseWithStatus instantiates a new response using the passed string and integer as the +// status and status code. +func NewResponseWithStatus(s string, c int) *http.Response { + resp := NewResponse() + resp.Status = s + resp.StatusCode = c + return resp +} + +// NewResponseWithBodyAndStatus instantiates a new response using the specified mock body, +// status and status code +func NewResponseWithBodyAndStatus(body *Body, c int, s string) *http.Response { + resp := NewResponse() + resp.Body = body + resp.ContentLength = body.Length() + resp.Status = s + resp.StatusCode = c + return resp +} + +// SetResponseHeader adds a header to the passed response. +func SetResponseHeader(resp *http.Response, h string, v string) { + if resp.Header == nil { + resp.Header = make(http.Header) + } + resp.Header.Set(h, v) +} + +// SetResponseHeaderValues adds a header containing all the passed string values. +func SetResponseHeaderValues(resp *http.Response, h string, values []string) { + if resp.Header == nil { + resp.Header = make(http.Header) + } + for _, v := range values { + resp.Header.Add(h, v) + } +} + +// SetAcceptedHeaders adds the headers usually associated with a 202 Accepted response. +func SetAcceptedHeaders(resp *http.Response) { + SetLocationHeader(resp, TestURL) + SetRetryHeader(resp, TestDelay) +} + +// SetLocationHeader adds the Location header. +func SetLocationHeader(resp *http.Response, location string) { + SetResponseHeader(resp, http.CanonicalHeaderKey(headerLocation), location) +} + +// SetRetryHeader adds the Retry-After header. +func SetRetryHeader(resp *http.Response, delay time.Duration) { + SetResponseHeader(resp, http.CanonicalHeaderKey(headerRetryAfter), fmt.Sprintf("%v", delay.Seconds())) +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/mocks/mocks.go b/vendor/github.com/Azure/go-autorest/autorest/mocks/mocks.go new file mode 100644 index 00000000000..a00a27dd82b --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/mocks/mocks.go @@ -0,0 +1,235 @@ +/* +Package mocks provides mocks and helpers used in testing. +*/ +package mocks + +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import ( + "fmt" + "io" + "net/http" + "time" +) + +// Body implements acceptable body over a string. +type Body struct { + s string + b []byte + isOpen bool + closeAttempts int +} + +// NewBody creates a new instance of Body. +func NewBody(s string) *Body { + return (&Body{s: s}).reset() +} + +// NewBodyWithBytes creates a new instance of Body. +func NewBodyWithBytes(b []byte) *Body { + return &Body{ + b: b, + isOpen: true, + } +} + +// NewBodyClose creates a new instance of Body. +func NewBodyClose(s string) *Body { + return &Body{s: s} +} + +// Read reads into the passed byte slice and returns the bytes read. +func (body *Body) Read(b []byte) (n int, err error) { + if !body.IsOpen() { + return 0, fmt.Errorf("ERROR: Body has been closed") + } + if len(body.b) == 0 { + return 0, io.EOF + } + n = copy(b, body.b) + body.b = body.b[n:] + return n, nil +} + +// Close closes the body. +func (body *Body) Close() error { + if body.isOpen { + body.isOpen = false + body.closeAttempts++ + } + return nil +} + +// CloseAttempts returns the number of times Close was called. +func (body *Body) CloseAttempts() int { + return body.closeAttempts +} + +// IsOpen returns true if the Body has not been closed, false otherwise. +func (body *Body) IsOpen() bool { + return body.isOpen +} + +func (body *Body) reset() *Body { + body.isOpen = true + body.b = []byte(body.s) + return body +} + +// Length returns the number of bytes in the body. +func (body *Body) Length() int64 { + if body == nil { + return 0 + } + return int64(len(body.b)) +} + +type response struct { + r *http.Response + e error + d time.Duration +} + +// Sender implements a simple null sender. +type Sender struct { + attempts int + responses []response + numResponses int + repeatResponse []int + err error + repeatError int + emitErrorAfter int +} + +// NewSender creates a new instance of Sender. +func NewSender() *Sender { + return &Sender{} +} + +// Do accepts the passed request and, based on settings, emits a response and possible error. +func (c *Sender) Do(r *http.Request) (resp *http.Response, err error) { + c.attempts++ + + if len(c.responses) > 0 { + resp = c.responses[0].r + if resp != nil { + if b, ok := resp.Body.(*Body); ok { + b.reset() + } + } else { + err = c.responses[0].e + } + time.Sleep(c.responses[0].d) + c.repeatResponse[0]-- + if c.repeatResponse[0] == 0 { + c.responses = c.responses[1:] + c.repeatResponse = c.repeatResponse[1:] + } + } else { + resp = NewResponse() + } + if resp != nil { + resp.Request = r + } + + if c.emitErrorAfter > 0 { + c.emitErrorAfter-- + } else if c.err != nil { + err = c.err + c.repeatError-- + if c.repeatError == 0 { + c.err = nil + } + } + + return +} + +// AppendResponse adds the passed http.Response to the response stack. +func (c *Sender) AppendResponse(resp *http.Response) { + c.AppendAndRepeatResponse(resp, 1) +} + +// AppendResponseWithDelay adds the passed http.Response to the response stack with the specified delay. +func (c *Sender) AppendResponseWithDelay(resp *http.Response, delay time.Duration) { + c.AppendAndRepeatResponseWithDelay(resp, delay, 1) +} + +// AppendAndRepeatResponse adds the passed http.Response to the response stack along with a +// repeat count. A negative repeat count will return the response for all remaining calls to Do. +func (c *Sender) AppendAndRepeatResponse(resp *http.Response, repeat int) { + c.appendAndRepeat(response{r: resp}, repeat) +} + +// AppendAndRepeatResponseWithDelay adds the passed http.Response to the response stack with the specified +// delay along with a repeat count. A negative repeat count will return the response for all remaining calls to Do. +func (c *Sender) AppendAndRepeatResponseWithDelay(resp *http.Response, delay time.Duration, repeat int) { + c.appendAndRepeat(response{r: resp, d: delay}, repeat) +} + +// AppendError adds the passed error to the response stack. +func (c *Sender) AppendError(err error) { + c.AppendAndRepeatError(err, 1) +} + +// AppendAndRepeatError adds the passed error to the response stack along with a repeat +// count. A negative repeat count will return the response for all remaining calls to Do. +func (c *Sender) AppendAndRepeatError(err error, repeat int) { + c.appendAndRepeat(response{e: err}, repeat) +} + +func (c *Sender) appendAndRepeat(resp response, repeat int) { + if c.responses == nil { + c.responses = []response{resp} + c.repeatResponse = []int{repeat} + } else { + c.responses = append(c.responses, resp) + c.repeatResponse = append(c.repeatResponse, repeat) + } + c.numResponses++ +} + +// Attempts returns the number of times Do was called. +func (c *Sender) Attempts() int { + return c.attempts +} + +// SetError sets the error Do should return. +func (c *Sender) SetError(err error) { + c.SetAndRepeatError(err, 1) +} + +// SetAndRepeatError sets the error Do should return and how many calls to Do will return the error. +// A negative repeat value will return the error for all remaining calls to Do. +func (c *Sender) SetAndRepeatError(err error, repeat int) { + c.err = err + c.repeatError = repeat +} + +// SetEmitErrorAfter sets the number of attempts to be made before errors are emitted. +func (c *Sender) SetEmitErrorAfter(ea int) { + c.emitErrorAfter = ea +} + +// NumResponses returns the number of responses that have been added to the sender. +func (c *Sender) NumResponses() int { + return c.numResponses +} + +// T is a simple testing struct. +type T struct { + Name string `json:"name" xml:"Name"` + Age int `json:"age" xml:"Age"` +} diff --git a/vendor/modules.txt b/vendor/modules.txt index a97fbffc809..20610a2a98f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -19,6 +19,8 @@ github.com/Azure/go-autorest/autorest/adal github.com/Azure/go-autorest/autorest/azure # github.com/Azure/go-autorest/autorest/date v0.1.0 => github.com/Azure/go-autorest/autorest/date v0.1.0 github.com/Azure/go-autorest/autorest/date +# github.com/Azure/go-autorest/autorest/mocks v0.2.0 => github.com/Azure/go-autorest/autorest/mocks v0.2.0 +github.com/Azure/go-autorest/autorest/mocks # github.com/Azure/go-autorest/autorest/to v0.2.0 => github.com/Azure/go-autorest/autorest/to v0.2.0 github.com/Azure/go-autorest/autorest/to # github.com/Azure/go-autorest/autorest/validation v0.1.0 => github.com/Azure/go-autorest/autorest/validation v0.1.0 @@ -1836,6 +1838,7 @@ k8s.io/kubelet/pkg/apis/pluginregistration/v1 k8s.io/legacy-cloud-providers/aws k8s.io/legacy-cloud-providers/azure k8s.io/legacy-cloud-providers/azure/auth +k8s.io/legacy-cloud-providers/azure/clients k8s.io/legacy-cloud-providers/azure/retry k8s.io/legacy-cloud-providers/gce k8s.io/legacy-cloud-providers/openstack