github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/azure/upgrades.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package azure
     5  
     6  import (
     7  	"strings"
     8  
     9  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
    10  	"github.com/juju/errors"
    11  
    12  	"github.com/juju/juju/environs"
    13  	"github.com/juju/juju/environs/context"
    14  	"github.com/juju/juju/environs/tags"
    15  	"github.com/juju/juju/provider/azure/internal/errorutils"
    16  )
    17  
    18  // UpgradeOperations is part of the upgrades.OperationSource interface.
    19  func (env *azureEnviron) UpgradeOperations(context.ProviderCallContext, environs.UpgradeOperationsParams) []environs.UpgradeOperation {
    20  	return []environs.UpgradeOperation{{
    21  		providerVersion1,
    22  		[]environs.UpgradeStep{
    23  			commonDeploymentUpgradeStep{env},
    24  		},
    25  	}}
    26  }
    27  
    28  // commonDeploymentUpgradeStep adds a "common" deployment to each
    29  // Environ corresponding to non-controller models.
    30  type commonDeploymentUpgradeStep struct {
    31  	env *azureEnviron
    32  }
    33  
    34  // Description is part of the environs.UpgradeStep interface.
    35  func (commonDeploymentUpgradeStep) Description() string {
    36  	return "Create common resource deployment"
    37  }
    38  
    39  // Run is part of the environs.UpgradeStep interface.
    40  func (step commonDeploymentUpgradeStep) Run(ctx context.ProviderCallContext) error {
    41  	env := step.env
    42  	isControllerEnviron, err := isControllerEnviron(env, ctx)
    43  	if err != nil {
    44  		return errors.Trace(err)
    45  	}
    46  	if isControllerEnviron {
    47  		// We only need to create the deployment for
    48  		// non-controller Environs.
    49  		return nil
    50  	}
    51  
    52  	// Identify the network security rules that exist already.
    53  	// We will add these, excluding the SSH/API rules, to the
    54  	// network security group template created in the deployment
    55  	// below.
    56  	securityGroups, err := env.securityGroupsClient()
    57  	if err != nil {
    58  		return errors.Trace(err)
    59  	}
    60  	allRules, err := existingSecurityRules(ctx, securityGroups, env.resourceGroup)
    61  	if errors.IsNotFound(err) {
    62  		allRules = nil
    63  	} else if err != nil {
    64  		return errors.Trace(err)
    65  	}
    66  	rules := make([]*armnetwork.SecurityRule, 0, len(allRules))
    67  	for _, rule := range allRules {
    68  		name := toValue(rule.Name)
    69  		if name == sshSecurityRuleName || strings.HasPrefix(name, apiSecurityRulePrefix) {
    70  			continue
    71  		}
    72  		rules = append(rules, rule)
    73  	}
    74  
    75  	return env.createCommonResourceDeployment(ctx, nil, rules)
    76  }
    77  
    78  // existingSecurityRules returns the network security rules for the internal
    79  // network security group in the specified resource group. If the network
    80  // security group has not been created, this function will return an error
    81  // satisfying errors.IsNotFound.
    82  func existingSecurityRules(
    83  	ctx context.ProviderCallContext,
    84  	nsgClient *armnetwork.SecurityGroupsClient,
    85  	resourceGroup string,
    86  ) ([]*armnetwork.SecurityRule, error) {
    87  	nsg, err := nsgClient.Get(ctx, resourceGroup, internalSecurityGroupName, nil)
    88  	if err != nil {
    89  		if errorutils.IsNotFoundError(err) {
    90  			return nil, errors.NotFoundf("security group")
    91  		}
    92  		return nil, errors.Annotate(err, "querying network security group")
    93  	}
    94  	var rules []*armnetwork.SecurityRule
    95  	if nsg.Properties != nil {
    96  		rules = nsg.Properties.SecurityRules
    97  	}
    98  	return rules, nil
    99  }
   100  
   101  func isControllerEnviron(env *azureEnviron, ctx context.ProviderCallContext) (bool, error) {
   102  	compute, err := env.computeClient()
   103  	if err != nil {
   104  		return false, errors.Trace(err)
   105  	}
   106  	// Look for a machine with the "juju-is-controller" tag set to "true".
   107  	pager := compute.NewListPager(env.resourceGroup, nil)
   108  	for pager.More() {
   109  		next, err := pager.NextPage(ctx)
   110  		if err != nil {
   111  			return false, errorutils.HandleCredentialError(errors.Annotate(err, "listing virtual machines"), ctx)
   112  		}
   113  		for _, vm := range next.Value {
   114  			if toValue(vm.Tags[tags.JujuIsController]) == "true" {
   115  				return true, nil
   116  			}
   117  		}
   118  	}
   119  	return false, nil
   120  }