builder/triton: Add a data source for source_machine_image
fixes: #5476 Based on this new template addition: ``` { "variables": { "image_version": "", "triton_account": "", "triton_key_id": "", "triton_key_material": "" }, "builders": [{ "type": "triton", "triton_account": "{{user `triton_account`}}", "triton_key_id": "{{user `triton_key_id`}}", "triton_key_material": "{{user `triton_key_material`}}", "source_machine_package": "g4-highcpu-128M", "source_machine_image_filter": { "name": "ubuntu-16.04", "most_recent": "true" }, "ssh_username": "root", "image_version": "{{user `image_version`}}", "image_name": "teamcity-server" }], "provisioners": [ { "type": "shell", "start_retry_timeout": "10m", "inline": [ "sudo apt-get update -y", "sudo apt-get install -y nginx" ] } ] } ``` I got the following output from packer: ``` packer-testing % make image packer build \ -var "triton_account=stack72_joyent" \ -var "triton_key_id=40:9d:d3:f9:0b:86:62:48:f4:2e:a5:8e:43:00:2a:9b" \ -var "triton_key_material=""" \ -var "image_version=1.0.0" \ new-template.json triton output will be in this color. ==> triton: Selecting an image based on search criteria ==> triton: Based, on given search criteria, Machine ID is: "7b5981c4-1889-11e7-b4c5-3f3bdfc9b88b" ==> triton: Waiting for source machine to become available... ==> triton: Waiting for SSH to become available... ==> triton: Connected to SSH! ==> triton: Provisioning with shell script: /var/folders/_p/2_zj9lqn4n11fx20qy787p7c0000gn/T/packer-shell797317310 triton: Get:1 http://security.ubuntu.com/ubuntu xenial-security InRelease [102 kB] triton: Hit:2 http://archive.ubuntu.com/ubuntu xenial InRelease ``` I can verify from the triton cli tools that the id `7b5981c4` (from the packer output) is indeed the correct ID ``` terraform [master●] % triton images name=~ubuntu-16.04 SHORTID NAME VERSION FLAGS OS TYPE PUBDATE 49b22aec ubuntu-16.04 20160427 P linux lx-dataset 2016-04-27 675834a0 ubuntu-16.04 20160505 P linux lx-dataset 2016-05-05 4edaa46a ubuntu-16.04 20160516 P linux lx-dataset 2016-05-16 05140a7e ubuntu-16.04 20160601 P linux lx-dataset 2016-06-01 e331b22a ubuntu-16.04 20161004 P linux lx-dataset 2016-10-04 8879c758 ubuntu-16.04 20161213 P linux lx-dataset 2016-12-13 7b5981c4 ubuntu-16.04 20170403 P linux lx-dataset 2017-04-03 <------- THIS IS THE LATEST UBUNTU IMAGE ```
This commit is contained in:
parent
a495948dc5
commit
7776bf596b
|
@ -5,6 +5,7 @@ import (
|
|||
)
|
||||
|
||||
type Driver interface {
|
||||
GetImage(config Config) (string, error)
|
||||
CreateImageFromMachine(machineId string, config Config) (string, error)
|
||||
CreateMachine(config Config) (string, error)
|
||||
DeleteImage(imageId string) error
|
||||
|
|
|
@ -17,6 +17,9 @@ type DriverMock struct {
|
|||
DeleteMachineId string
|
||||
DeleteMachineErr error
|
||||
|
||||
GetImageId string
|
||||
GetImageErr error
|
||||
|
||||
GetMachineErr error
|
||||
|
||||
StopMachineId string
|
||||
|
@ -29,6 +32,14 @@ type DriverMock struct {
|
|||
WaitForMachineStateErr error
|
||||
}
|
||||
|
||||
func (d *DriverMock) GetImage(config Config) (string, error) {
|
||||
if d.GetImageErr != nil {
|
||||
return "", d.GetImageErr
|
||||
}
|
||||
|
||||
return config.MachineImage, nil
|
||||
}
|
||||
|
||||
func (d *DriverMock) CreateImageFromMachine(machineId string, config Config) (string, error) {
|
||||
if d.CreateImageFromMachineErr != nil {
|
||||
return "", d.CreateImageFromMachineErr
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/joyent/triton-go/compute"
|
||||
|
@ -28,6 +30,36 @@ func NewDriverTriton(ui packer.Ui, config Config) (Driver, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (d *driverTriton) GetImage(config Config) (string, error) {
|
||||
computeClient, _ := d.client.Compute()
|
||||
images, err := computeClient.Images().List(context.Background(), &compute.ListImagesInput{
|
||||
Name: config.MachineImageFilters.Name,
|
||||
OS: config.MachineImageFilters.OS,
|
||||
Version: config.MachineImageFilters.Version,
|
||||
Public: config.MachineImageFilters.Public,
|
||||
Type: config.MachineImageFilters.Type,
|
||||
State: config.MachineImageFilters.State,
|
||||
Owner: config.MachineImageFilters.Owner,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(images) == 0 {
|
||||
return "", errors.New("No images found in your search. Please refine your search criteria")
|
||||
}
|
||||
|
||||
if len(images) > 1 {
|
||||
if !config.MachineImageFilters.MostRecent {
|
||||
return "", errors.New("More than 1 machine image was found in your search. Please refine your search criteria")
|
||||
} else {
|
||||
return mostRecentImages(images).ID, nil
|
||||
}
|
||||
} else {
|
||||
return images[0].ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *driverTriton) CreateImageFromMachine(machineId string, config Config) (string, error) {
|
||||
computeClient, _ := d.client.Compute()
|
||||
image, err := computeClient.Images().CreateFromMachine(context.Background(), &compute.CreateImageFromMachineInput{
|
||||
|
@ -193,3 +225,29 @@ func waitFor(f func() (bool, error), every, timeout time.Duration) error {
|
|||
|
||||
return errors.New("Timed out while waiting for resource change")
|
||||
}
|
||||
|
||||
func mostRecentImages(images []*compute.Image) *compute.Image {
|
||||
return sortImages(images)[0]
|
||||
}
|
||||
|
||||
type imageSort []*compute.Image
|
||||
|
||||
func sortImages(images []*compute.Image) []*compute.Image {
|
||||
sortedImages := images
|
||||
sort.Sort(sort.Reverse(imageSort(sortedImages)))
|
||||
return sortedImages
|
||||
}
|
||||
|
||||
func (a imageSort) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a imageSort) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
func (a imageSort) Less(i, j int) bool {
|
||||
itime := a[i].PublishedAt
|
||||
jtime := a[j].PublishedAt
|
||||
return itime.Unix() < jtime.Unix()
|
||||
}
|
||||
|
|
|
@ -9,13 +9,29 @@ import (
|
|||
// SourceMachineConfig represents the configuration to run a machine using
|
||||
// the SDC API in order for provisioning to take place.
|
||||
type SourceMachineConfig struct {
|
||||
MachineName string `mapstructure:"source_machine_name"`
|
||||
MachinePackage string `mapstructure:"source_machine_package"`
|
||||
MachineImage string `mapstructure:"source_machine_image"`
|
||||
MachineNetworks []string `mapstructure:"source_machine_networks"`
|
||||
MachineMetadata map[string]string `mapstructure:"source_machine_metadata"`
|
||||
MachineTags map[string]string `mapstructure:"source_machine_tags"`
|
||||
MachineFirewallEnabled bool `mapstructure:"source_machine_firewall_enabled"`
|
||||
MachineName string `mapstructure:"source_machine_name"`
|
||||
MachinePackage string `mapstructure:"source_machine_package"`
|
||||
MachineImage string `mapstructure:"source_machine_image"`
|
||||
MachineNetworks []string `mapstructure:"source_machine_networks"`
|
||||
MachineMetadata map[string]string `mapstructure:"source_machine_metadata"`
|
||||
MachineTags map[string]string `mapstructure:"source_machine_tags"`
|
||||
MachineFirewallEnabled bool `mapstructure:"source_machine_firewall_enabled"`
|
||||
MachineImageFilters MachineImageFilter `mapstructure:"source_machine_image_filter"`
|
||||
}
|
||||
|
||||
type MachineImageFilter struct {
|
||||
MostRecent bool `mapstructure:"most_recent"`
|
||||
Name string
|
||||
OS string
|
||||
Version string
|
||||
Public bool
|
||||
State string
|
||||
Owner string
|
||||
Type string
|
||||
}
|
||||
|
||||
func (m *MachineImageFilter) Empty() bool {
|
||||
return m.Name == "" && m.OS == "" && m.Version == "" && m.State == "" && m.Owner == "" && m.Type == ""
|
||||
}
|
||||
|
||||
// Prepare performs basic validation on a SourceMachineConfig struct.
|
||||
|
@ -26,8 +42,8 @@ func (c *SourceMachineConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
errs = append(errs, fmt.Errorf("A source_machine_package must be specified"))
|
||||
}
|
||||
|
||||
if c.MachineImage == "" {
|
||||
errs = append(errs, fmt.Errorf("A source_machine_image must be specified"))
|
||||
if c.MachineImage != "" && c.MachineImageFilters.Name != "" {
|
||||
errs = append(errs, fmt.Errorf("You cannot specify a Machine Image and also Machine Name filter"))
|
||||
}
|
||||
|
||||
if c.MachineNetworks == nil {
|
||||
|
|
|
@ -24,13 +24,6 @@ func TestSourceMachineConfig_Prepare(t *testing.T) {
|
|||
if errs == nil {
|
||||
t.Fatalf("should error: %#v", sc)
|
||||
}
|
||||
|
||||
sc = testSourceMachineConfig(t)
|
||||
sc.MachineImage = ""
|
||||
errs = sc.Prepare(nil)
|
||||
if errs == nil {
|
||||
t.Fatalf("should error: %#v", sc)
|
||||
}
|
||||
}
|
||||
|
||||
func testSourceMachineConfig(t *testing.T) SourceMachineConfig {
|
||||
|
|
|
@ -17,7 +17,16 @@ func (s *StepCreateSourceMachine) Run(state multistep.StateBag) multistep.StepAc
|
|||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Creating source machine...")
|
||||
if !config.MachineImageFilters.Empty() {
|
||||
ui.Say("Selecting an image based on search criteria")
|
||||
imageId, err := driver.GetImage(config)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Problem selecting an image based on an search criteria: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Based, on given search criteria, Machine ID is: %q", imageId))
|
||||
config.MachineImage = imageId
|
||||
}
|
||||
|
||||
machineId, err := driver.CreateMachine(config)
|
||||
if err != nil {
|
||||
|
@ -33,7 +42,6 @@ func (s *StepCreateSourceMachine) Run(state multistep.StateBag) multistep.StepAc
|
|||
}
|
||||
|
||||
state.Put("machine", machineId)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,8 @@ builder.
|
|||
base image automatically decides the brand. On the Joyent public cloud a
|
||||
valid `source_machine_image` could for example be
|
||||
`70e3ae72-96b6-11e6-9056-9737fd4d0764` for version 16.3.1 of the 64bit
|
||||
SmartOS base image (a 'joyent' brand image).
|
||||
SmartOS base image (a 'joyent' brand image). `source_machine_image_filter` can
|
||||
be used to populate this UUID.
|
||||
|
||||
- `source_machine_package` (string) - The Triton package to use while building
|
||||
the image. Does not affect (and does not have to be the same) as the package
|
||||
|
@ -133,6 +134,19 @@ builder.
|
|||
information about the image. Maximum 128 characters.
|
||||
- `image_tags` (object of key/value strings) - Tag applied to the image.
|
||||
|
||||
- `source_machine_image_filter` (object) - Filters used to populate the `source_machine_image` field.
|
||||
Example:
|
||||
|
||||
``` json
|
||||
{
|
||||
"source_machine_image_filter": {
|
||||
"name": "ubuntu-16.04",
|
||||
"type": "lx-dataset",
|
||||
"most_recent": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Basic Example
|
||||
|
||||
Below is a minimal example to create an joyent-brand image on the Joyent public
|
||||
|
@ -149,7 +163,11 @@ cloud:
|
|||
|
||||
"source_machine_name": "image-builder",
|
||||
"source_machine_package": "g4-highcpu-128M",
|
||||
"source_machine_image": "f6acf198-2037-11e7-8863-8fdd4ce58b6a",
|
||||
"source_machine_image_filter": {
|
||||
"name": "ubuntu-16.04",
|
||||
"type": "lx-dataset",
|
||||
"most_recent": "true"
|
||||
},
|
||||
|
||||
"ssh_username": "root",
|
||||
|
||||
|
|
Loading…
Reference in New Issue