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 }