github.com/mitchellh/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  }