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 }