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  }