sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/loadbalancers/client.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package loadbalancers 18 19 import ( 20 "context" 21 "time" 22 23 "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" 24 "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" 25 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" 26 "github.com/pkg/errors" 27 "sigs.k8s.io/cluster-api-provider-azure/azure" 28 "sigs.k8s.io/cluster-api-provider-azure/azure/services/async" 29 "sigs.k8s.io/cluster-api-provider-azure/util/tele" 30 ) 31 32 // azureClient contains the Azure go-sdk Client. 33 type azureClient struct { 34 loadbalancers *armnetwork.LoadBalancersClient 35 auth azure.Authorizer 36 apiCallTimeout time.Duration 37 } 38 39 // newClient creates a new load balancer client from an authorizer. 40 func newClient(auth azure.Authorizer, apiCallTimeout time.Duration) (*azureClient, error) { 41 opts, err := azure.ARMClientOptions(auth.CloudEnvironment()) 42 if err != nil { 43 return nil, errors.Wrap(err, "failed to get load balancer client options") 44 } 45 46 factory, err := armnetwork.NewClientFactory(auth.SubscriptionID(), auth.Token(), opts) 47 if err != nil { 48 return nil, errors.Wrap(err, "failed to create armnetwork client factory") 49 } 50 return &azureClient{factory.NewLoadBalancersClient(), auth, apiCallTimeout}, nil 51 } 52 53 // Get gets the specified load balancer. 54 func (ac *azureClient) Get(ctx context.Context, spec azure.ResourceSpecGetter) (result interface{}, err error) { 55 ctx, _, done := tele.StartSpanWithLogger(ctx, "loadbalancers.azureClient.Get") 56 defer done() 57 58 resp, err := ac.loadbalancers.Get(ctx, spec.ResourceGroupName(), spec.ResourceName(), nil) 59 if err != nil { 60 return nil, err 61 } 62 63 return resp.LoadBalancer, nil 64 } 65 66 // CreateOrUpdateAsync creates or updates a load balancer asynchronously. 67 // It sends a PUT request to Azure and if accepted without error, the func will return a Poller which can be used to track the ongoing 68 // progress of the operation. 69 func (ac *azureClient) CreateOrUpdateAsync(ctx context.Context, spec azure.ResourceSpecGetter, resumeToken string, parameters interface{}) (result interface{}, poller *runtime.Poller[armnetwork.LoadBalancersClientCreateOrUpdateResponse], err error) { 70 ctx, _, done := tele.StartSpanWithLogger(ctx, "loadbalancers.azureClient.CreateOrUpdate") 71 defer done() 72 73 loadBalancer, ok := parameters.(armnetwork.LoadBalancer) 74 if !ok && parameters != nil { 75 return nil, nil, errors.Errorf("%T is not an armnetwork.LoadBalancer", parameters) 76 } 77 78 var extraPolicies []policy.Policy 79 if loadBalancer.Etag != nil { 80 extraPolicies = append(extraPolicies, azure.CustomPutPatchHeaderPolicy{ 81 Headers: map[string]string{ 82 "If-Match": *loadBalancer.Etag, 83 }, 84 }) 85 } 86 87 // Create a new client that knows how to add etag headers to the request. 88 clientOpts, err := azure.ARMClientOptions(ac.auth.CloudEnvironment(), extraPolicies...) 89 if err != nil { 90 return nil, nil, errors.Wrap(err, "failed to create loadbalancer client options") 91 } 92 93 factory, err := armnetwork.NewClientFactory(ac.auth.SubscriptionID(), ac.auth.Token(), clientOpts) 94 if err != nil { 95 return nil, nil, errors.Wrap(err, "failed to create armnetwork client factory") 96 } 97 98 client := factory.NewLoadBalancersClient() 99 opts := &armnetwork.LoadBalancersClientBeginCreateOrUpdateOptions{ResumeToken: resumeToken} 100 poller, err = client.BeginCreateOrUpdate(ctx, spec.ResourceGroupName(), spec.ResourceName(), loadBalancer, opts) 101 if err != nil { 102 return nil, nil, err 103 } 104 105 ctx, cancel := context.WithTimeout(ctx, ac.apiCallTimeout) 106 defer cancel() 107 108 pollOpts := &runtime.PollUntilDoneOptions{Frequency: async.DefaultPollerFrequency} 109 resp, err := poller.PollUntilDone(ctx, pollOpts) 110 if err != nil { 111 // if an error occurs, return the poller. 112 // This means the long-running operation didn't finish in the specified timeout. 113 return nil, poller, err 114 } 115 116 // if the operation completed, return nil poller. 117 return resp.LoadBalancer, nil, err 118 } 119 120 // DeleteAsync deletes a load balancer asynchronously. DeleteAsync sends a DELETE 121 // request to Azure and if accepted without error, the func will return a Poller which can be used to track the ongoing 122 // progress of the operation. 123 func (ac *azureClient) DeleteAsync(ctx context.Context, spec azure.ResourceSpecGetter, resumeToken string) (poller *runtime.Poller[armnetwork.LoadBalancersClientDeleteResponse], err error) { 124 ctx, _, done := tele.StartSpanWithLogger(ctx, "loadbalancers.azureClient.Delete") 125 defer done() 126 127 opts := &armnetwork.LoadBalancersClientBeginDeleteOptions{ResumeToken: resumeToken} 128 poller, err = ac.loadbalancers.BeginDelete(ctx, spec.ResourceGroupName(), spec.ResourceName(), opts) 129 if err != nil { 130 return nil, err 131 } 132 133 ctx, cancel := context.WithTimeout(ctx, ac.apiCallTimeout) 134 defer cancel() 135 136 pollOpts := &runtime.PollUntilDoneOptions{Frequency: async.DefaultPollerFrequency} 137 _, err = poller.PollUntilDone(ctx, pollOpts) 138 if err != nil { 139 // if error occurs, return the poller. 140 // this means the long-running operation didn't finish in the specified timeout. 141 return poller, err 142 } 143 // if the operation completed, return nil poller. 144 return nil, err 145 }