github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/triton/driver_triton.go (about)

     1  package triton
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/http"
     7  	"time"
     8  
     9  	"github.com/hashicorp/packer/packer"
    10  	"github.com/joyent/triton-go"
    11  )
    12  
    13  type driverTriton struct {
    14  	client *triton.Client
    15  	ui     packer.Ui
    16  }
    17  
    18  func NewDriverTriton(ui packer.Ui, config Config) (Driver, error) {
    19  	client, err := config.AccessConfig.CreateTritonClient()
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  
    24  	return &driverTriton{
    25  		client: client,
    26  		ui:     ui,
    27  	}, nil
    28  }
    29  
    30  func (d *driverTriton) CreateImageFromMachine(machineId string, config Config) (string, error) {
    31  	image, err := d.client.Images().CreateImageFromMachine(context.Background(), &triton.CreateImageFromMachineInput{
    32  		MachineID:   machineId,
    33  		Name:        config.ImageName,
    34  		Version:     config.ImageVersion,
    35  		Description: config.ImageDescription,
    36  		HomePage:    config.ImageHomepage,
    37  		EULA:        config.ImageEULA,
    38  		ACL:         config.ImageACL,
    39  		Tags:        config.ImageTags,
    40  	})
    41  	if err != nil {
    42  		return "", err
    43  	}
    44  
    45  	return image.ID, err
    46  }
    47  
    48  func (d *driverTriton) CreateMachine(config Config) (string, error) {
    49  	input := &triton.CreateMachineInput{
    50  		Package:         config.MachinePackage,
    51  		Image:           config.MachineImage,
    52  		Metadata:        config.MachineMetadata,
    53  		Tags:            config.MachineTags,
    54  		FirewallEnabled: config.MachineFirewallEnabled,
    55  	}
    56  
    57  	if config.MachineName == "" {
    58  		// If not supplied generate a name for the source VM: "packer-builder-[image_name]".
    59  		// The version is not used because it can contain characters invalid for a VM name.
    60  		input.Name = "packer-builder-" + config.ImageName
    61  	} else {
    62  		input.Name = config.MachineName
    63  	}
    64  
    65  	if len(config.MachineNetworks) > 0 {
    66  		input.Networks = config.MachineNetworks
    67  	}
    68  
    69  	machine, err := d.client.Machines().CreateMachine(context.Background(), input)
    70  	if err != nil {
    71  		return "", err
    72  	}
    73  
    74  	return machine.ID, nil
    75  }
    76  
    77  func (d *driverTriton) DeleteImage(imageId string) error {
    78  	return d.client.Images().DeleteImage(context.Background(), &triton.DeleteImageInput{
    79  		ImageID: imageId,
    80  	})
    81  }
    82  
    83  func (d *driverTriton) DeleteMachine(machineId string) error {
    84  	return d.client.Machines().DeleteMachine(context.Background(), &triton.DeleteMachineInput{
    85  		ID: machineId,
    86  	})
    87  }
    88  
    89  func (d *driverTriton) GetMachineIP(machineId string) (string, error) {
    90  	machine, err := d.client.Machines().GetMachine(context.Background(), &triton.GetMachineInput{
    91  		ID: machineId,
    92  	})
    93  	if err != nil {
    94  		return "", err
    95  	}
    96  
    97  	return machine.PrimaryIP, nil
    98  }
    99  
   100  func (d *driverTriton) StopMachine(machineId string) error {
   101  	return d.client.Machines().StopMachine(context.Background(), &triton.StopMachineInput{
   102  		MachineID: machineId,
   103  	})
   104  }
   105  
   106  // waitForMachineState uses the supplied client to wait for the state of
   107  // the machine with the given ID to reach the state described in state.
   108  // If timeout is reached before the machine reaches the required state, an
   109  // error is returned. If the machine reaches the target state within the
   110  // timeout, nil is returned.
   111  func (d *driverTriton) WaitForMachineState(machineId string, state string, timeout time.Duration) error {
   112  	return waitFor(
   113  		func() (bool, error) {
   114  			machine, err := d.client.Machines().GetMachine(context.Background(), &triton.GetMachineInput{
   115  				ID: machineId,
   116  			})
   117  			if machine == nil {
   118  				return false, err
   119  			}
   120  			return machine.State == state, err
   121  		},
   122  		3*time.Second,
   123  		timeout,
   124  	)
   125  }
   126  
   127  // waitForMachineDeletion uses the supplied client to wait for the machine
   128  // with the given ID to be deleted. It is expected that the API call to delete
   129  // the machine has already been issued at this point.
   130  func (d *driverTriton) WaitForMachineDeletion(machineId string, timeout time.Duration) error {
   131  	return waitFor(
   132  		func() (bool, error) {
   133  			_, err := d.client.Machines().GetMachine(context.Background(), &triton.GetMachineInput{
   134  				ID: machineId,
   135  			})
   136  			if err != nil {
   137  				// Return true only when we receive a 410 (Gone) response.  A 404
   138  				// indicates that the machine is being deleted whereas a 410 indicates
   139  				// that this process has completed.
   140  				if triErr, ok := err.(*triton.TritonError); ok && triErr.StatusCode == http.StatusGone {
   141  					return true, nil
   142  				}
   143  			}
   144  
   145  			return false, err
   146  		},
   147  		3*time.Second,
   148  		timeout,
   149  	)
   150  }
   151  
   152  func (d *driverTriton) WaitForImageCreation(imageId string, timeout time.Duration) error {
   153  	return waitFor(
   154  		func() (bool, error) {
   155  			image, err := d.client.Images().GetImage(context.Background(), &triton.GetImageInput{
   156  				ImageID: imageId,
   157  			})
   158  			if image == nil {
   159  				return false, err
   160  			}
   161  			return image.OS != "", err
   162  		},
   163  		3*time.Second,
   164  		timeout,
   165  	)
   166  }
   167  
   168  func waitFor(f func() (bool, error), every, timeout time.Duration) error {
   169  	start := time.Now()
   170  
   171  	for time.Since(start) <= timeout {
   172  		stop, err := f()
   173  		if err != nil {
   174  			return err
   175  		}
   176  
   177  		if stop {
   178  			return nil
   179  		}
   180  
   181  		time.Sleep(every)
   182  	}
   183  
   184  	return errors.New("Timed out while waiting for resource change")
   185  }