github.phpd.cn/hashicorp/packer@v1.3.2/builder/azure/arm/step_deploy_template.go (about) 1 package arm 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/url" 8 "strings" 9 10 "github.com/hashicorp/packer/builder/azure/common/constants" 11 "github.com/hashicorp/packer/helper/multistep" 12 "github.com/hashicorp/packer/packer" 13 ) 14 15 type StepDeployTemplate struct { 16 client *AzureClient 17 deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error 18 delete func(ctx context.Context, client *AzureClient, resourceType string, resourceName string, resourceGroupName string) error 19 disk func(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) 20 deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error 21 say func(message string) 22 error func(e error) 23 config *Config 24 factory templateFactoryFunc 25 name string 26 } 27 28 func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, config *Config, deploymentName string, factory templateFactoryFunc) *StepDeployTemplate { 29 var step = &StepDeployTemplate{ 30 client: client, 31 say: func(message string) { ui.Say(message) }, 32 error: func(e error) { ui.Error(e.Error()) }, 33 config: config, 34 factory: factory, 35 name: deploymentName, 36 } 37 38 step.deploy = step.deployTemplate 39 step.delete = deleteResource 40 step.disk = step.getImageDetails 41 step.deleteDisk = step.deleteImage 42 return step 43 } 44 45 func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error { 46 deployment, err := s.factory(s.config) 47 if err != nil { 48 return err 49 } 50 51 f, err := s.client.DeploymentsClient.CreateOrUpdate(ctx, resourceGroupName, deploymentName, *deployment) 52 if err == nil { 53 err = f.WaitForCompletion(ctx, s.client.DeploymentsClient.Client) 54 } 55 if err != nil { 56 s.say(s.client.LastError.Error()) 57 } 58 return err 59 } 60 61 func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { 62 s.say("Deploying deployment template ...") 63 64 var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) 65 66 s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) 67 s.say(fmt.Sprintf(" -> DeploymentName : '%s'", s.name)) 68 69 return processStepResult( 70 s.deploy(ctx, resourceGroupName, s.name), 71 s.error, state) 72 } 73 74 func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) { 75 //We can't depend on constants.ArmOSDiskVhd being set 76 var imageName string 77 var imageType string 78 vm, err := s.client.VirtualMachinesClient.Get(ctx, resourceGroupName, computeName, "") 79 if err != nil { 80 return imageName, imageType, err 81 } else { 82 if vm.StorageProfile.OsDisk.Vhd != nil { 83 imageType = "image" 84 imageName = *vm.StorageProfile.OsDisk.Vhd.URI 85 } else { 86 imageType = "Microsoft.Compute/disks" 87 imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID 88 } 89 } 90 return imageType, imageName, nil 91 } 92 93 //TODO(paulmey): move to helpers file 94 func deleteResource(ctx context.Context, client *AzureClient, resourceType string, resourceName string, resourceGroupName string) error { 95 switch resourceType { 96 case "Microsoft.Compute/virtualMachines": 97 f, err := client.VirtualMachinesClient.Delete(ctx, resourceGroupName, resourceName) 98 if err == nil { 99 err = f.WaitForCompletion(ctx, client.VirtualMachinesClient.Client) 100 } 101 return err 102 case "Microsoft.KeyVault/vaults": 103 // TODO(paulmey): not sure why VaultClient doesn't do cancellation 104 _, err := client.VaultClientDelete.Delete(resourceGroupName, resourceName) 105 return err 106 case "Microsoft.Network/networkInterfaces": 107 f, err := client.InterfacesClient.Delete(ctx, resourceGroupName, resourceName) 108 if err == nil { 109 err = f.WaitForCompletion(ctx, client.InterfacesClient.Client) 110 } 111 return err 112 case "Microsoft.Network/virtualNetworks": 113 f, err := client.VirtualNetworksClient.Delete(ctx, resourceGroupName, resourceName) 114 if err == nil { 115 err = f.WaitForCompletion(ctx, client.VirtualNetworksClient.Client) 116 } 117 return err 118 case "Microsoft.Network/publicIPAddresses": 119 f, err := client.PublicIPAddressesClient.Delete(ctx, resourceGroupName, resourceName) 120 if err == nil { 121 err = f.WaitForCompletion(ctx, client.PublicIPAddressesClient.Client) 122 } 123 return err 124 } 125 return nil 126 } 127 128 func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageType string, imageName string, resourceGroupName string) error { 129 // Managed disk 130 if imageType == "Microsoft.Compute/disks" { 131 xs := strings.Split(imageName, "/") 132 diskName := xs[len(xs)-1] 133 f, err := s.client.DisksClient.Delete(ctx, resourceGroupName, diskName) 134 if err == nil { 135 err = f.WaitForCompletion(ctx, s.client.DisksClient.Client) 136 } 137 return err 138 } 139 // VHD image 140 u, err := url.Parse(imageName) 141 if err != nil { 142 return err 143 } 144 xs := strings.Split(u.Path, "/") 145 if len(xs) < 3 { 146 return errors.New("Unable to parse path of image " + imageName) 147 } 148 var storageAccountName = xs[1] 149 var blobName = strings.Join(xs[2:], "/") 150 151 blob := s.client.BlobStorageClient.GetContainerReference(storageAccountName).GetBlobReference(blobName) 152 err = blob.Delete(nil) 153 return err 154 } 155 156 func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { 157 //Only clean up if this was an existing resource group and the resource group 158 //is marked as created 159 var existingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool) 160 var resourceGroupCreated = state.Get(constants.ArmIsResourceGroupCreated).(bool) 161 if !existingResourceGroup || !resourceGroupCreated { 162 return 163 } 164 ui := state.Get("ui").(packer.Ui) 165 ui.Say("\nThe resource group was not created by Packer, deleting individual resources ...") 166 167 var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) 168 var computeName = state.Get(constants.ArmComputeName).(string) 169 var deploymentName = s.name 170 imageType, imageName, err := s.disk(context.TODO(), resourceGroupName, computeName) 171 if err != nil { 172 ui.Error("Could not retrieve OS Image details") 173 } 174 175 ui.Say(" -> Deployment: " + deploymentName) 176 if deploymentName != "" { 177 maxResources := int32(50) 178 deploymentOperations, err := s.client.DeploymentOperationsClient.ListComplete(context.TODO(), resourceGroupName, deploymentName, &maxResources) 179 if err != nil { 180 ui.Error(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+ 181 "Name: %s\n"+ 182 "Error: %s", resourceGroupName, err)) 183 } 184 for deploymentOperations.NotDone() { 185 deploymentOperation := deploymentOperations.Value() 186 // Sometimes an empty operation is added to the list by Azure 187 if deploymentOperation.Properties.TargetResource == nil { 188 deploymentOperations.Next() 189 continue 190 } 191 ui.Say(fmt.Sprintf(" -> %s : '%s'", 192 *deploymentOperation.Properties.TargetResource.ResourceType, 193 *deploymentOperation.Properties.TargetResource.ResourceName)) 194 err = s.delete(context.TODO(), s.client, 195 *deploymentOperation.Properties.TargetResource.ResourceType, 196 *deploymentOperation.Properties.TargetResource.ResourceName, 197 resourceGroupName) 198 if err != nil { 199 ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ 200 "Name: %s\n"+ 201 "Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err)) 202 } 203 if err = deploymentOperations.Next(); err != nil { 204 ui.Error(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+ 205 "Name: %s\n"+ 206 "Error: %s", resourceGroupName, err)) 207 break 208 } 209 } 210 211 // The disk is not defined as an operation in the template so has to be 212 // deleted separately 213 ui.Say(fmt.Sprintf(" -> %s : '%s'", imageType, imageName)) 214 err = s.deleteDisk(context.TODO(), imageType, imageName, resourceGroupName) 215 if err != nil { 216 ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+ 217 "Name: %s\n"+ 218 "Error: %s", imageName, err)) 219 } 220 } 221 }