github.phpd.cn/hashicorp/packer@v1.3.2/builder/triton/driver_triton.go (about) 1 package triton 2 3 import ( 4 "context" 5 "errors" 6 "net/http" 7 "sort" 8 "time" 9 10 "github.com/hashicorp/packer/packer" 11 "github.com/joyent/triton-go/compute" 12 terrors "github.com/joyent/triton-go/errors" 13 ) 14 15 type driverTriton struct { 16 client *Client 17 ui packer.Ui 18 } 19 20 func NewDriverTriton(ui packer.Ui, config Config) (Driver, error) { 21 client, err := config.AccessConfig.CreateTritonClient() 22 if err != nil { 23 return nil, err 24 } 25 26 return &driverTriton{ 27 client: client, 28 ui: ui, 29 }, nil 30 } 31 32 func (d *driverTriton) GetImage(config Config) (string, error) { 33 computeClient, _ := d.client.Compute() 34 images, err := computeClient.Images().List(context.Background(), &compute.ListImagesInput{ 35 Name: config.MachineImageFilters.Name, 36 OS: config.MachineImageFilters.OS, 37 Version: config.MachineImageFilters.Version, 38 Public: config.MachineImageFilters.Public, 39 Type: config.MachineImageFilters.Type, 40 State: config.MachineImageFilters.State, 41 Owner: config.MachineImageFilters.Owner, 42 }) 43 if err != nil { 44 return "", err 45 } 46 47 if len(images) == 0 { 48 return "", errors.New("No images found in your search. Please refine your search criteria") 49 } 50 51 if len(images) > 1 { 52 if !config.MachineImageFilters.MostRecent { 53 return "", errors.New("More than 1 machine image was found in your search. Please refine your search criteria") 54 } else { 55 return mostRecentImages(images).ID, nil 56 } 57 } else { 58 return images[0].ID, nil 59 } 60 } 61 62 func (d *driverTriton) CreateImageFromMachine(machineId string, config Config) (string, error) { 63 computeClient, _ := d.client.Compute() 64 image, err := computeClient.Images().CreateFromMachine(context.Background(), &compute.CreateImageFromMachineInput{ 65 MachineID: machineId, 66 Name: config.ImageName, 67 Version: config.ImageVersion, 68 Description: config.ImageDescription, 69 HomePage: config.ImageHomepage, 70 EULA: config.ImageEULA, 71 ACL: config.ImageACL, 72 Tags: config.ImageTags, 73 }) 74 if err != nil { 75 return "", err 76 } 77 78 return image.ID, err 79 } 80 81 func (d *driverTriton) CreateMachine(config Config) (string, error) { 82 computeClient, _ := d.client.Compute() 83 input := &compute.CreateInstanceInput{ 84 Package: config.MachinePackage, 85 Image: config.MachineImage, 86 Metadata: config.MachineMetadata, 87 Tags: config.MachineTags, 88 FirewallEnabled: config.MachineFirewallEnabled, 89 } 90 91 if config.MachineName == "" { 92 // If not supplied generate a name for the source VM: "packer-builder-[image_name]". 93 // The version is not used because it can contain characters invalid for a VM name. 94 input.Name = "packer-builder-" + config.ImageName 95 } else { 96 input.Name = config.MachineName 97 } 98 99 if len(config.MachineNetworks) > 0 { 100 input.Networks = config.MachineNetworks 101 } 102 103 machine, err := computeClient.Instances().Create(context.Background(), input) 104 if err != nil { 105 return "", err 106 } 107 108 return machine.ID, nil 109 } 110 111 func (d *driverTriton) DeleteImage(imageId string) error { 112 computeClient, _ := d.client.Compute() 113 return computeClient.Images().Delete(context.Background(), &compute.DeleteImageInput{ 114 ImageID: imageId, 115 }) 116 } 117 118 func (d *driverTriton) DeleteMachine(machineId string) error { 119 computeClient, _ := d.client.Compute() 120 return computeClient.Instances().Delete(context.Background(), &compute.DeleteInstanceInput{ 121 ID: machineId, 122 }) 123 } 124 125 func (d *driverTriton) GetMachineIP(machineId string) (string, error) { 126 computeClient, _ := d.client.Compute() 127 machine, err := computeClient.Instances().Get(context.Background(), &compute.GetInstanceInput{ 128 ID: machineId, 129 }) 130 if err != nil { 131 return "", err 132 } 133 134 return machine.PrimaryIP, nil 135 } 136 137 func (d *driverTriton) StopMachine(machineId string) error { 138 computeClient, _ := d.client.Compute() 139 return computeClient.Instances().Stop(context.Background(), &compute.StopInstanceInput{ 140 InstanceID: machineId, 141 }) 142 } 143 144 // waitForMachineState uses the supplied client to wait for the state of 145 // the machine with the given ID to reach the state described in state. 146 // If timeout is reached before the machine reaches the required state, an 147 // error is returned. If the machine reaches the target state within the 148 // timeout, nil is returned. 149 func (d *driverTriton) WaitForMachineState(machineId string, state string, timeout time.Duration) error { 150 return waitFor( 151 func() (bool, error) { 152 computeClient, _ := d.client.Compute() 153 machine, err := computeClient.Instances().Get(context.Background(), &compute.GetInstanceInput{ 154 ID: machineId, 155 }) 156 if machine == nil { 157 return false, err 158 } 159 return machine.State == state, err 160 }, 161 3*time.Second, 162 timeout, 163 ) 164 } 165 166 // waitForMachineDeletion uses the supplied client to wait for the machine 167 // with the given ID to be deleted. It is expected that the API call to delete 168 // the machine has already been issued at this point. 169 func (d *driverTriton) WaitForMachineDeletion(machineId string, timeout time.Duration) error { 170 return waitFor( 171 func() (bool, error) { 172 computeClient, _ := d.client.Compute() 173 _, err := computeClient.Instances().Get(context.Background(), &compute.GetInstanceInput{ 174 ID: machineId, 175 }) 176 if err != nil { 177 // Return true only when we receive a 410 (Gone) response. A 404 178 // indicates that the machine is being deleted whereas a 410 indicates 179 // that this process has completed. 180 if terrors.IsSpecificStatusCode(err, http.StatusGone) { 181 return true, nil 182 } 183 } 184 185 return false, err 186 }, 187 3*time.Second, 188 timeout, 189 ) 190 } 191 192 func (d *driverTriton) WaitForImageCreation(imageId string, timeout time.Duration) error { 193 return waitFor( 194 func() (bool, error) { 195 computeClient, _ := d.client.Compute() 196 image, err := computeClient.Images().Get(context.Background(), &compute.GetImageInput{ 197 ImageID: imageId, 198 }) 199 if image == nil { 200 return false, err 201 } 202 return image.State == "active", err 203 }, 204 3*time.Second, 205 timeout, 206 ) 207 } 208 209 func waitFor(f func() (bool, error), every, timeout time.Duration) error { 210 start := time.Now() 211 212 for time.Since(start) <= timeout { 213 stop, err := f() 214 if err != nil { 215 return err 216 } 217 218 if stop { 219 return nil 220 } 221 222 time.Sleep(every) 223 } 224 225 return errors.New("Timed out while waiting for resource change") 226 } 227 228 func mostRecentImages(images []*compute.Image) *compute.Image { 229 return sortImages(images)[0] 230 } 231 232 type imageSort []*compute.Image 233 234 func sortImages(images []*compute.Image) []*compute.Image { 235 sortedImages := images 236 sort.Sort(sort.Reverse(imageSort(sortedImages))) 237 return sortedImages 238 } 239 240 func (a imageSort) Len() int { 241 return len(a) 242 } 243 244 func (a imageSort) Swap(i, j int) { 245 a[i], a[j] = a[j], a[i] 246 } 247 248 func (a imageSort) Less(i, j int) bool { 249 itime := a[i].PublishedAt 250 jtime := a[j].PublishedAt 251 return itime.Unix() < jtime.Unix() 252 }