1// +build !providerless 2 3/* 4Copyright 2020 The Kubernetes Authors. 5 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17*/ 18 19package publicipclient 20 21import ( 22 "context" 23 "fmt" 24 "net/http" 25 "time" 26 27 "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" 28 "github.com/Azure/go-autorest/autorest" 29 "github.com/Azure/go-autorest/autorest/azure" 30 "github.com/Azure/go-autorest/autorest/to" 31 32 "k8s.io/client-go/util/flowcontrol" 33 "k8s.io/klog/v2" 34 azclients "k8s.io/legacy-cloud-providers/azure/clients" 35 "k8s.io/legacy-cloud-providers/azure/clients/armclient" 36 "k8s.io/legacy-cloud-providers/azure/metrics" 37 "k8s.io/legacy-cloud-providers/azure/retry" 38) 39 40var _ Interface = &Client{} 41 42// Client implements PublicIPAddress client Interface. 43type Client struct { 44 armClient armclient.Interface 45 subscriptionID string 46 47 // Rate limiting configures. 48 rateLimiterReader flowcontrol.RateLimiter 49 rateLimiterWriter flowcontrol.RateLimiter 50 51 // ARM throttling configures. 52 RetryAfterReader time.Time 53 RetryAfterWriter time.Time 54} 55 56// New creates a new PublicIPAddress client with ratelimiting. 57func New(config *azclients.ClientConfig) *Client { 58 baseURI := config.ResourceManagerEndpoint 59 authorizer := config.Authorizer 60 armClient := armclient.New(authorizer, baseURI, config.UserAgent, APIVersion, config.Location, config.Backoff) 61 rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig) 62 63 klog.V(2).Infof("Azure PublicIPAddressesClient (read ops) using rate limit config: QPS=%g, bucket=%d", 64 config.RateLimitConfig.CloudProviderRateLimitQPS, 65 config.RateLimitConfig.CloudProviderRateLimitBucket) 66 klog.V(2).Infof("Azure PublicIPAddressesClient (write ops) using rate limit config: QPS=%g, bucket=%d", 67 config.RateLimitConfig.CloudProviderRateLimitQPSWrite, 68 config.RateLimitConfig.CloudProviderRateLimitBucketWrite) 69 70 client := &Client{ 71 armClient: armClient, 72 rateLimiterReader: rateLimiterReader, 73 rateLimiterWriter: rateLimiterWriter, 74 subscriptionID: config.SubscriptionID, 75 } 76 77 return client 78} 79 80// Get gets a PublicIPAddress. 81func (c *Client) Get(ctx context.Context, resourceGroupName string, publicIPAddressName string, expand string) (network.PublicIPAddress, *retry.Error) { 82 mc := metrics.NewMetricContext("public_ip_addresses", "get", resourceGroupName, c.subscriptionID, "") 83 84 // Report errors if the client is rate limited. 85 if !c.rateLimiterReader.TryAccept() { 86 mc.RateLimitedCount() 87 return network.PublicIPAddress{}, retry.GetRateLimitError(false, "PublicIPGet") 88 } 89 90 // Report errors if the client is throttled. 91 if c.RetryAfterReader.After(time.Now()) { 92 mc.ThrottledCount() 93 rerr := retry.GetThrottlingError("PublicIPGet", "client throttled", c.RetryAfterReader) 94 return network.PublicIPAddress{}, rerr 95 } 96 97 result, rerr := c.getPublicIPAddress(ctx, resourceGroupName, publicIPAddressName, expand) 98 mc.Observe(rerr.Error()) 99 if rerr != nil { 100 if rerr.IsThrottled() { 101 // Update RetryAfterReader so that no more requests would be sent until RetryAfter expires. 102 c.RetryAfterReader = rerr.RetryAfter 103 } 104 105 return result, rerr 106 } 107 108 return result, nil 109} 110 111// getPublicIPAddress gets a PublicIPAddress. 112func (c *Client) getPublicIPAddress(ctx context.Context, resourceGroupName string, publicIPAddressName string, expand string) (network.PublicIPAddress, *retry.Error) { 113 resourceID := armclient.GetResourceID( 114 c.subscriptionID, 115 resourceGroupName, 116 "Microsoft.Network/publicIPAddresses", 117 publicIPAddressName, 118 ) 119 result := network.PublicIPAddress{} 120 121 response, rerr := c.armClient.GetResource(ctx, resourceID, expand) 122 defer c.armClient.CloseResponse(ctx, response) 123 if rerr != nil { 124 klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "publicip.get.request", resourceID, rerr.Error()) 125 return result, rerr 126 } 127 128 err := autorest.Respond( 129 response, 130 azure.WithErrorUnlessStatusCode(http.StatusOK), 131 autorest.ByUnmarshallingJSON(&result)) 132 if err != nil { 133 klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "publicip.get.respond", resourceID, err) 134 return result, retry.GetError(response, err) 135 } 136 137 result.Response = autorest.Response{Response: response} 138 return result, nil 139} 140 141// GetVirtualMachineScaleSetPublicIPAddress gets a PublicIPAddress for VMSS VM. 142func (c *Client) GetVirtualMachineScaleSetPublicIPAddress(ctx context.Context, resourceGroupName string, virtualMachineScaleSetName string, virtualmachineIndex string, networkInterfaceName string, IPConfigurationName string, publicIPAddressName string, expand string) (network.PublicIPAddress, *retry.Error) { 143 mc := metrics.NewMetricContext("vmss_public_ip_addresses", "get", resourceGroupName, c.subscriptionID, "") 144 145 // Report errors if the client is rate limited. 146 if !c.rateLimiterReader.TryAccept() { 147 mc.RateLimitedCount() 148 return network.PublicIPAddress{}, retry.GetRateLimitError(false, "VMSSPublicIPGet") 149 } 150 151 // Report errors if the client is throttled. 152 if c.RetryAfterReader.After(time.Now()) { 153 mc.ThrottledCount() 154 rerr := retry.GetThrottlingError("VMSSPublicIPGet", "client throttled", c.RetryAfterReader) 155 return network.PublicIPAddress{}, rerr 156 } 157 158 result, rerr := c.getVMSSPublicIPAddress(ctx, resourceGroupName, virtualMachineScaleSetName, virtualmachineIndex, networkInterfaceName, IPConfigurationName, publicIPAddressName, expand) 159 mc.Observe(rerr.Error()) 160 if rerr != nil { 161 if rerr.IsThrottled() { 162 // Update RetryAfterReader so that no more requests would be sent until RetryAfter expires. 163 c.RetryAfterReader = rerr.RetryAfter 164 } 165 166 return result, rerr 167 } 168 169 return result, nil 170} 171 172// getVMSSPublicIPAddress gets a PublicIPAddress for VMSS VM. 173func (c *Client) getVMSSPublicIPAddress(ctx context.Context, resourceGroupName string, virtualMachineScaleSetName string, virtualmachineIndex string, networkInterfaceName string, IPConfigurationName string, publicIPAddressName string, expand string) (network.PublicIPAddress, *retry.Error) { 174 resourceID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachineScaleSets/%s/virtualMachines/%s/networkInterfaces/%s/ipconfigurations/%s/publicipaddresses/%s", 175 autorest.Encode("path", c.subscriptionID), 176 autorest.Encode("path", resourceGroupName), 177 autorest.Encode("path", virtualMachineScaleSetName), 178 autorest.Encode("path", virtualmachineIndex), 179 autorest.Encode("path", networkInterfaceName), 180 autorest.Encode("path", IPConfigurationName), 181 autorest.Encode("path", publicIPAddressName), 182 ) 183 184 result := network.PublicIPAddress{} 185 queryParameters := map[string]interface{}{ 186 "api-version": ComputeAPIVersion, 187 } 188 if len(expand) > 0 { 189 queryParameters["$expand"] = autorest.Encode("query", expand) 190 } 191 decorators := []autorest.PrepareDecorator{ 192 autorest.WithQueryParameters(queryParameters), 193 } 194 response, rerr := c.armClient.GetResourceWithDecorators(ctx, resourceID, decorators) 195 defer c.armClient.CloseResponse(ctx, response) 196 if rerr != nil { 197 klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "vmsspublicip.get.request", resourceID, rerr.Error()) 198 return result, rerr 199 } 200 201 err := autorest.Respond( 202 response, 203 azure.WithErrorUnlessStatusCode(http.StatusOK), 204 autorest.ByUnmarshallingJSON(&result)) 205 if err != nil { 206 klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "vmsspublicip.get.respond", resourceID, err) 207 return result, retry.GetError(response, err) 208 } 209 210 result.Response = autorest.Response{Response: response} 211 return result, nil 212} 213 214// List gets a list of PublicIPAddress in the resource group. 215func (c *Client) List(ctx context.Context, resourceGroupName string) ([]network.PublicIPAddress, *retry.Error) { 216 mc := metrics.NewMetricContext("public_ip_addresses", "list", resourceGroupName, c.subscriptionID, "") 217 218 // Report errors if the client is rate limited. 219 if !c.rateLimiterReader.TryAccept() { 220 mc.RateLimitedCount() 221 return nil, retry.GetRateLimitError(false, "PublicIPList") 222 } 223 224 // Report errors if the client is throttled. 225 if c.RetryAfterReader.After(time.Now()) { 226 mc.ThrottledCount() 227 rerr := retry.GetThrottlingError("PublicIPList", "client throttled", c.RetryAfterReader) 228 return nil, rerr 229 } 230 231 result, rerr := c.listPublicIPAddress(ctx, resourceGroupName) 232 mc.Observe(rerr.Error()) 233 if rerr != nil { 234 if rerr.IsThrottled() { 235 // Update RetryAfterReader so that no more requests would be sent until RetryAfter expires. 236 c.RetryAfterReader = rerr.RetryAfter 237 } 238 239 return result, rerr 240 } 241 242 return result, nil 243} 244 245// listPublicIPAddress gets a list of PublicIPAddress in the resource group. 246func (c *Client) listPublicIPAddress(ctx context.Context, resourceGroupName string) ([]network.PublicIPAddress, *retry.Error) { 247 resourceID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/publicIPAddresses", 248 autorest.Encode("path", c.subscriptionID), 249 autorest.Encode("path", resourceGroupName)) 250 result := make([]network.PublicIPAddress, 0) 251 page := &PublicIPAddressListResultPage{} 252 page.fn = c.listNextResults 253 254 resp, rerr := c.armClient.GetResource(ctx, resourceID, "") 255 defer c.armClient.CloseResponse(ctx, resp) 256 if rerr != nil { 257 klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "publicip.list.request", resourceID, rerr.Error()) 258 return result, rerr 259 } 260 261 var err error 262 page.pialr, err = c.listResponder(resp) 263 if err != nil { 264 klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "publicip.list.respond", resourceID, err) 265 return result, retry.GetError(resp, err) 266 } 267 268 for { 269 result = append(result, page.Values()...) 270 271 // Abort the loop when there's no nextLink in the response. 272 if to.String(page.Response().NextLink) == "" { 273 break 274 } 275 276 if err = page.NextWithContext(ctx); err != nil { 277 klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "publicip.list.next", resourceID, err) 278 return result, retry.GetError(page.Response().Response.Response, err) 279 } 280 } 281 282 return result, nil 283} 284 285// CreateOrUpdate creates or updates a PublicIPAddress. 286func (c *Client) CreateOrUpdate(ctx context.Context, resourceGroupName string, publicIPAddressName string, parameters network.PublicIPAddress) *retry.Error { 287 mc := metrics.NewMetricContext("public_ip_addresses", "create_or_update", resourceGroupName, c.subscriptionID, "") 288 289 // Report errors if the client is rate limited. 290 if !c.rateLimiterWriter.TryAccept() { 291 mc.RateLimitedCount() 292 return retry.GetRateLimitError(true, "PublicIPCreateOrUpdate") 293 } 294 295 // Report errors if the client is throttled. 296 if c.RetryAfterWriter.After(time.Now()) { 297 mc.ThrottledCount() 298 rerr := retry.GetThrottlingError("PublicIPCreateOrUpdate", "client throttled", c.RetryAfterWriter) 299 return rerr 300 } 301 302 rerr := c.createOrUpdatePublicIP(ctx, resourceGroupName, publicIPAddressName, parameters) 303 mc.Observe(rerr.Error()) 304 if rerr != nil { 305 if rerr.IsThrottled() { 306 // Update RetryAfterReader so that no more requests would be sent until RetryAfter expires. 307 c.RetryAfterWriter = rerr.RetryAfter 308 } 309 310 return rerr 311 } 312 313 return nil 314} 315 316// createOrUpdatePublicIP creates or updates a PublicIPAddress. 317func (c *Client) createOrUpdatePublicIP(ctx context.Context, resourceGroupName string, publicIPAddressName string, parameters network.PublicIPAddress) *retry.Error { 318 resourceID := armclient.GetResourceID( 319 c.subscriptionID, 320 resourceGroupName, 321 "Microsoft.Network/publicIPAddresses", 322 publicIPAddressName, 323 ) 324 325 response, rerr := c.armClient.PutResource(ctx, resourceID, parameters) 326 defer c.armClient.CloseResponse(ctx, response) 327 if rerr != nil { 328 klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "publicip.put.request", resourceID, rerr.Error()) 329 return rerr 330 } 331 332 if response != nil && response.StatusCode != http.StatusNoContent { 333 _, rerr = c.createOrUpdateResponder(response) 334 if rerr != nil { 335 klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "publicip.put.respond", resourceID, rerr.Error()) 336 return rerr 337 } 338 } 339 340 return nil 341} 342 343func (c *Client) createOrUpdateResponder(resp *http.Response) (*network.PublicIPAddress, *retry.Error) { 344 result := &network.PublicIPAddress{} 345 err := autorest.Respond( 346 resp, 347 azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), 348 autorest.ByUnmarshallingJSON(&result)) 349 result.Response = autorest.Response{Response: resp} 350 return result, retry.GetError(resp, err) 351} 352 353// Delete deletes a PublicIPAddress by name. 354func (c *Client) Delete(ctx context.Context, resourceGroupName string, publicIPAddressName string) *retry.Error { 355 mc := metrics.NewMetricContext("public_ip_addresses", "delete", resourceGroupName, c.subscriptionID, "") 356 357 // Report errors if the client is rate limited. 358 if !c.rateLimiterWriter.TryAccept() { 359 mc.RateLimitedCount() 360 return retry.GetRateLimitError(true, "PublicIPDelete") 361 } 362 363 // Report errors if the client is throttled. 364 if c.RetryAfterWriter.After(time.Now()) { 365 mc.ThrottledCount() 366 rerr := retry.GetThrottlingError("PublicIPDelete", "client throttled", c.RetryAfterWriter) 367 return rerr 368 } 369 370 rerr := c.deletePublicIP(ctx, resourceGroupName, publicIPAddressName) 371 mc.Observe(rerr.Error()) 372 if rerr != nil { 373 if rerr.IsThrottled() { 374 // Update RetryAfterReader so that no more requests would be sent until RetryAfter expires. 375 c.RetryAfterWriter = rerr.RetryAfter 376 } 377 378 return rerr 379 } 380 381 return nil 382} 383 384// deletePublicIP deletes a PublicIPAddress by name. 385func (c *Client) deletePublicIP(ctx context.Context, resourceGroupName string, publicIPAddressName string) *retry.Error { 386 resourceID := armclient.GetResourceID( 387 c.subscriptionID, 388 resourceGroupName, 389 "Microsoft.Network/publicIPAddresses", 390 publicIPAddressName, 391 ) 392 393 return c.armClient.DeleteResource(ctx, resourceID, "") 394} 395 396func (c *Client) listResponder(resp *http.Response) (result network.PublicIPAddressListResult, err error) { 397 err = autorest.Respond( 398 resp, 399 autorest.ByIgnoring(), 400 azure.WithErrorUnlessStatusCode(http.StatusOK), 401 autorest.ByUnmarshallingJSON(&result)) 402 result.Response = autorest.Response{Response: resp} 403 return 404} 405 406// publicIPAddressListResultPreparer prepares a request to retrieve the next set of results. 407// It returns nil if no more results exist. 408func (c *Client) publicIPAddressListResultPreparer(ctx context.Context, lr network.PublicIPAddressListResult) (*http.Request, error) { 409 if lr.NextLink == nil || len(to.String(lr.NextLink)) < 1 { 410 return nil, nil 411 } 412 413 decorators := []autorest.PrepareDecorator{ 414 autorest.WithBaseURL(to.String(lr.NextLink)), 415 } 416 return c.armClient.PrepareGetRequest(ctx, decorators...) 417} 418 419// listNextResults retrieves the next set of results, if any. 420func (c *Client) listNextResults(ctx context.Context, lastResults network.PublicIPAddressListResult) (result network.PublicIPAddressListResult, err error) { 421 req, err := c.publicIPAddressListResultPreparer(ctx, lastResults) 422 if err != nil { 423 return result, autorest.NewErrorWithError(err, "publicipclient", "listNextResults", nil, "Failure preparing next results request") 424 } 425 if req == nil { 426 return 427 } 428 429 resp, rerr := c.armClient.Send(ctx, req) 430 defer c.armClient.CloseResponse(ctx, resp) 431 if rerr != nil { 432 result.Response = autorest.Response{Response: resp} 433 return result, autorest.NewErrorWithError(rerr.Error(), "publicipclient", "listNextResults", resp, "Failure sending next results request") 434 } 435 436 result, err = c.listResponder(resp) 437 if err != nil { 438 err = autorest.NewErrorWithError(err, "publicipclient", "listNextResults", resp, "Failure responding to next results request") 439 } 440 441 return 442} 443 444// PublicIPAddressListResultPage contains a page of PublicIPAddress values. 445type PublicIPAddressListResultPage struct { 446 fn func(context.Context, network.PublicIPAddressListResult) (network.PublicIPAddressListResult, error) 447 pialr network.PublicIPAddressListResult 448} 449 450// NextWithContext advances to the next page of values. If there was an error making 451// the request the page does not advance and the error is returned. 452func (page *PublicIPAddressListResultPage) NextWithContext(ctx context.Context) (err error) { 453 next, err := page.fn(ctx, page.pialr) 454 if err != nil { 455 return err 456 } 457 page.pialr = next 458 return nil 459} 460 461// Next advances to the next page of values. If there was an error making 462// the request the page does not advance and the error is returned. 463// Deprecated: Use NextWithContext() instead. 464func (page *PublicIPAddressListResultPage) Next() error { 465 return page.NextWithContext(context.Background()) 466} 467 468// NotDone returns true if the page enumeration should be started or is not yet complete. 469func (page PublicIPAddressListResultPage) NotDone() bool { 470 return !page.pialr.IsEmpty() 471} 472 473// Response returns the raw server response from the last page request. 474func (page PublicIPAddressListResultPage) Response() network.PublicIPAddressListResult { 475 return page.pialr 476} 477 478// Values returns the slice of values for the current page or nil if there are no values. 479func (page PublicIPAddressListResultPage) Values() []network.PublicIPAddress { 480 if page.pialr.IsEmpty() { 481 return nil 482 } 483 return *page.pialr.Value 484} 485