feat(builder/hcloud): allow selecting image based on filters

This commit is contained in:
Andre Hilsendeger 2019-08-05 10:10:59 +02:00
parent d832cd6f67
commit cb1e30ef69
2 changed files with 67 additions and 8 deletions

View File

@ -25,10 +25,11 @@ type Config struct {
Endpoint string `mapstructure:"endpoint"`
PollInterval time.Duration `mapstructure:"poll_interval"`
ServerName string `mapstructure:"server_name"`
Location string `mapstructure:"location"`
ServerType string `mapstructure:"server_type"`
Image string `mapstructure:"image"`
ServerName string `mapstructure:"server_name"`
Location string `mapstructure:"location"`
ServerType string `mapstructure:"server_type"`
Image string `mapstructure:"image"`
ImageFilter *imageFilter `mapstructure:"image_filter"`
SnapshotName string `mapstructure:"snapshot_name"`
SnapshotLabels map[string]string `mapstructure:"snapshot_labels"`
@ -41,6 +42,11 @@ type Config struct {
ctx interpolate.Context
}
type imageFilter struct {
WithSelector []string `mapstructure:"with_selector"`
MostRecent bool `mapstructure:"most_recent"`
}
func NewConfig(raws ...interface{}) (*Config, []string, error) {
c := new(Config)
@ -108,9 +114,18 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
errs, errors.New("server type is required"))
}
if c.Image == "" {
if c.Image == "" && c.ImageFilter == nil {
errs = packer.MultiErrorAppend(
errs, errors.New("image is required"))
errs, errors.New("image or image_filter is required"))
}
if c.ImageFilter != nil {
if len(c.ImageFilter.WithSelector) == 0 {
errs = packer.MultiErrorAppend(
errs, errors.New("image_filter.with_selector is required when specifying filter"))
} else if c.Image != "" {
errs = packer.MultiErrorAppend(
errs, errors.New("only one of image or image_filter can be specified"))
}
}
if c.UserData != "" && c.UserDataFile != "" {

View File

@ -3,8 +3,9 @@ package hcloud
import (
"context"
"fmt"
"io/ioutil"
"sort"
"strings"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
@ -50,10 +51,24 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu
sshKeys = append(sshKeys, sshKey)
}
var image *hcloud.Image
if c.Image != "" {
image = &hcloud.Image{Name: c.Image}
} else {
var err error
image, err = getImageWithSelectors(ctx, client, c)
if err != nil {
ui.Error(err.Error())
state.Put("error", err)
return multistep.ActionHalt
}
ui.Message(fmt.Sprintf("Using image %s with ID %d", image.Description, image.ID))
}
serverCreateResult, _, err := client.Server.Create(ctx, hcloud.ServerCreateOpts{
Name: c.ServerName,
ServerType: &hcloud.ServerType{Name: c.ServerType},
Image: &hcloud.Image{Name: c.Image},
Image: image,
SSHKeys: sshKeys,
Location: &hcloud.Location{Name: c.Location},
UserData: userData,
@ -185,3 +200,32 @@ func waitForAction(ctx context.Context, client *hcloud.Client, action *hcloud.Ac
}
return nil
}
func getImageWithSelectors(ctx context.Context, client *hcloud.Client, c *Config) (*hcloud.Image, error) {
var allImages []*hcloud.Image
var selector = strings.Join(c.ImageFilter.WithSelector, ",")
opts := hcloud.ImageListOpts{
ListOpts: hcloud.ListOpts{LabelSelector: selector},
Status: []hcloud.ImageStatus{hcloud.ImageStatusAvailable},
}
allImages, err := client.Image.AllWithOpts(ctx, opts)
if err != nil {
return nil, err
}
if len(allImages) == 0 {
return nil, fmt.Errorf("no image found for selector %q", selector)
}
if len(allImages) > 1 {
if !c.ImageFilter.MostRecent {
return nil, fmt.Errorf("more than one image found for selector %q", selector)
}
sort.Slice(allImages, func(i, j int) bool {
return allImages[i].Created.After(allImages[j].Created)
})
}
return allImages[0], nil
}