github.com/amanya/packer@v0.12.1-0.20161117214323-902ac5ab2eb6/builder/cloudstack/step_create_instance.go (about)

     1  package cloudstack
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/mitchellh/multistep"
     9  	"github.com/mitchellh/packer/packer"
    10  	"github.com/xanzy/go-cloudstack/cloudstack"
    11  )
    12  
    13  // stepCreateInstance represents a Packer build step that creates CloudStack instances.
    14  type stepCreateInstance struct{}
    15  
    16  // Run executes the Packer build step that creates a CloudStack instance.
    17  func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction {
    18  	client := state.Get("client").(*cloudstack.CloudStackClient)
    19  	config := state.Get("config").(*Config)
    20  	ui := state.Get("ui").(packer.Ui)
    21  
    22  	ui.Say("Creating instance...")
    23  
    24  	// Create a new parameter struct.
    25  	p := client.VirtualMachine.NewDeployVirtualMachineParams(
    26  		config.ServiceOffering,
    27  		config.instanceSource,
    28  		config.Zone,
    29  	)
    30  
    31  	// Configure the instance.
    32  	p.SetName(config.InstanceName)
    33  	p.SetDisplayname("Created by Packer")
    34  
    35  	if config.Keypair != "" {
    36  		p.SetKeypair(config.Keypair)
    37  	}
    38  
    39  	// If we use an ISO, configure the disk offering.
    40  	if config.SourceISO != "" {
    41  		p.SetDiskofferingid(config.DiskOffering)
    42  		p.SetHypervisor(config.Hypervisor)
    43  	}
    44  
    45  	// If we use a template, set the root disk size.
    46  	if config.SourceTemplate != "" && config.DiskSize > 0 {
    47  		p.SetRootdisksize(config.DiskSize)
    48  	}
    49  
    50  	// Retrieve the zone object.
    51  	zone, _, err := client.Zone.GetZoneByID(config.Zone)
    52  	if err != nil {
    53  		ui.Error(err.Error())
    54  		return multistep.ActionHalt
    55  	}
    56  
    57  	if zone.Networktype == "Advanced" {
    58  		// Set the network ID's.
    59  		p.SetNetworkids([]string{config.Network})
    60  	}
    61  
    62  	// If there is a project supplied, set the project id.
    63  	if config.Project != "" {
    64  		p.SetProjectid(config.Project)
    65  	}
    66  
    67  	if config.UserData != "" {
    68  		ud, err := getUserData(config.UserData, config.HTTPGetOnly)
    69  		if err != nil {
    70  			ui.Error(err.Error())
    71  			return multistep.ActionHalt
    72  		}
    73  
    74  		p.SetUserdata(ud)
    75  	}
    76  
    77  	// Create the new instance.
    78  	instance, err := client.VirtualMachine.DeployVirtualMachine(p)
    79  	if err != nil {
    80  		ui.Error(fmt.Sprintf("Error creating new instance %s: %s", config.InstanceName, err))
    81  		return multistep.ActionHalt
    82  	}
    83  
    84  	ui.Message("Instance has been created!")
    85  
    86  	// Set the auto generated password if a password was not explicitly configured.
    87  	switch config.Comm.Type {
    88  	case "ssh":
    89  		if config.Comm.SSHPassword == "" {
    90  			config.Comm.SSHPassword = instance.Password
    91  		}
    92  	case "winrm":
    93  		if config.Comm.WinRMPassword == "" {
    94  			config.Comm.WinRMPassword = instance.Password
    95  		}
    96  	}
    97  
    98  	// Set the host address when using the local IP address to connect.
    99  	if config.UseLocalIPAddress {
   100  		config.hostAddress = instance.Nic[0].Ipaddress
   101  	}
   102  
   103  	// Store the instance ID so we can remove it later.
   104  	state.Put("instance_id", instance.Id)
   105  
   106  	return multistep.ActionContinue
   107  }
   108  
   109  // Cleanup any resources that may have been created during the Run phase.
   110  func (s *stepCreateInstance) Cleanup(state multistep.StateBag) {
   111  	client := state.Get("client").(*cloudstack.CloudStackClient)
   112  	ui := state.Get("ui").(packer.Ui)
   113  
   114  	instanceID, ok := state.Get("instance_id").(string)
   115  	if !ok || instanceID == "" {
   116  		return
   117  	}
   118  
   119  	// Create a new parameter struct.
   120  	p := client.VirtualMachine.NewDestroyVirtualMachineParams(instanceID)
   121  
   122  	// Set expunge so the instance is completely removed
   123  	p.SetExpunge(true)
   124  
   125  	ui.Say("Deleting instance...")
   126  	if _, err := client.VirtualMachine.DestroyVirtualMachine(p); err != nil {
   127  		// This is a very poor way to be told the ID does no longer exist :(
   128  		if strings.Contains(err.Error(), fmt.Sprintf(
   129  			"Invalid parameter id value=%s due to incorrect long value format, "+
   130  				"or entity does not exist", instanceID)) {
   131  			return
   132  		}
   133  
   134  		ui.Error(fmt.Sprintf("Error destroying instance: %s", err))
   135  	}
   136  
   137  	ui.Message("Instance has been deleted!")
   138  
   139  	return
   140  }
   141  
   142  // getUserData returns the user data as a base64 encoded string.
   143  func getUserData(userData string, httpGETOnly bool) (string, error) {
   144  	ud := base64.StdEncoding.EncodeToString([]byte(userData))
   145  
   146  	// deployVirtualMachine uses POST by default, so max userdata is 32K
   147  	maxUD := 32768
   148  
   149  	if httpGETOnly {
   150  		// deployVirtualMachine using GET instead, so max userdata is 2K
   151  		maxUD = 2048
   152  	}
   153  
   154  	if len(ud) > maxUD {
   155  		return "", fmt.Errorf(
   156  			"The supplied user_data contains %d bytes after encoding, "+
   157  				"this exeeds the limit of %d bytes", len(ud), maxUD)
   158  	}
   159  
   160  	return ud, nil
   161  }