Updated tag to slice, docs, comments, only active images, source_image_name supercedes filter name

This commit is contained in:
Tom Carrio 2018-07-16 13:00:02 -04:00
parent e776ad51a9
commit de9999ecb9
5 changed files with 26 additions and 35 deletions

View File

@ -59,19 +59,9 @@ func getImageVisibility(s string) (images.ImageVisibility, error) {
// Retrieve the specific ImageVisibility using the exported const from images // Retrieve the specific ImageVisibility using the exported const from images
func getImageStatus(s string) (images.ImageStatus, error) { func getImageStatus(s string) (images.ImageStatus, error) {
statuses := [...]images.ImageStatus{ activeStatus := images.ImageStatusActive
images.ImageStatusActive, if string(activeStatus) == s {
images.ImageStatusDeactivated, return activeStatus, nil
images.ImageStatusDeleted,
images.ImageStatusPendingDelete,
images.ImageStatusQueued,
images.ImageStatusSaving,
}
for _, status := range statuses {
if string(status) == s {
return status, nil
}
} }
var nilStatus images.ImageStatus var nilStatus images.ImageStatus
@ -80,7 +70,7 @@ func getImageStatus(s string) (images.ImageStatus, error) {
// Allows construction of all fields from ListOpts using the "q" tags and // Allows construction of all fields from ListOpts using the "q" tags and
// type detection to set all fields within a provided ListOpts struct // type detection to set all fields within a provided ListOpts struct
func buildImageFilters(input map[string]string, listOpts *images.ListOpts) *packer.MultiError { func buildImageFilters(input map[string]interface{}, listOpts *images.ListOpts) *packer.MultiError {
// fill each field in the ListOpts based on tag/type // fill each field in the ListOpts based on tag/type
metaOpts := reflect.Indirect(reflect.ValueOf(listOpts)) metaOpts := reflect.Indirect(reflect.ValueOf(listOpts))
@ -99,7 +89,7 @@ func buildImageFilters(input map[string]string, listOpts *images.ListOpts) *pack
// Handles integer types used in ListOpts // Handles integer types used in ListOpts
case reflect.Int64, reflect.Int: case reflect.Int64, reflect.Int:
iVal, err := strconv.Atoi(val) iVal, err := strconv.Atoi(val.(string))
if err != nil { if err != nil {
multierror.Append(err, multiErr.Errors...) multierror.Append(err, multiErr.Errors...)
continue continue
@ -120,7 +110,7 @@ func buildImageFilters(input map[string]string, listOpts *images.ListOpts) *pack
vField.Set(reflect.ValueOf(val)) vField.Set(reflect.ValueOf(val))
case reflect.TypeOf(images.ImageVisibility("")): case reflect.TypeOf(images.ImageVisibility("")):
iv, err := getImageVisibility(val) iv, err := getImageVisibility(val.(string))
if err != nil { if err != nil {
multierror.Append(err, multiErr.Errors...) multierror.Append(err, multiErr.Errors...)
continue continue
@ -128,7 +118,7 @@ func buildImageFilters(input map[string]string, listOpts *images.ListOpts) *pack
vField.Set(reflect.ValueOf(iv)) vField.Set(reflect.ValueOf(iv))
case reflect.TypeOf(images.ImageStatus("")): case reflect.TypeOf(images.ImageStatus("")):
is, err := getImageStatus(val) is, err := getImageStatus(val.(string))
if err != nil { if err != nil {
multierror.Append(err, multiErr.Errors...) multierror.Append(err, multiErr.Errors...)
continue continue
@ -138,14 +128,7 @@ func buildImageFilters(input map[string]string, listOpts *images.ListOpts) *pack
// Generates slice of strings for Tags // Generates slice of strings for Tags
case reflect.Slice: case reflect.Slice:
typeOfSlice := reflect.TypeOf(vField).Elem() vField.Set(reflect.ValueOf(val))
fieldArray := reflect.MakeSlice(reflect.SliceOf(typeOfSlice), 0, 0)
for _, s := range strings.Split(val, ",") {
if len(s) > 0 {
fieldArray = reflect.Append(fieldArray, reflect.ValueOf(s))
}
}
vField.Set(fieldArray)
default: default:
multierror.Append( multierror.Append(
@ -158,7 +141,7 @@ func buildImageFilters(input map[string]string, listOpts *images.ListOpts) *pack
fieldName == reflect.TypeOf(listOpts.UpdatedAtQuery).Name() { fieldName == reflect.TypeOf(listOpts.UpdatedAtQuery).Name() {
// get ImageDateQuery from string and set to this field // get ImageDateQuery from string and set to this field
query, err := dateToImageDateQuery(key, val) query, err := dateToImageDateQuery(key, val.(string))
if err != nil { if err != nil {
multierror.Append(err, multiErr.Errors...) multierror.Append(err, multiErr.Errors...)
continue continue

View File

@ -30,7 +30,7 @@ func TestGetImageFilter(t *testing.T) {
func TestBuildImageFilter(t *testing.T) { func TestBuildImageFilter(t *testing.T) {
testOpts := images.ListOpts{} testOpts := images.ListOpts{}
filters := map[string]string{ filters := map[string]interface{}{
"limit": "3", "limit": "3",
"name": "Ubuntu 16.04", "name": "Ubuntu 16.04",
"visibility": "public", "visibility": "public",
@ -127,12 +127,14 @@ func TestImageFilterOptionsDecode(t *testing.T) {
"most_recent": true, "most_recent": true,
"filters": map[string]interface{}{ "filters": map[string]interface{}{
"visibility": "protected", "visibility": "protected",
"tag": "prod", "tag": []string{"prod","ready"},
"name": "ubuntu 16.04", "name": "ubuntu 16.04",
}, },
} }
err := mapstructure.Decode(input, &opts) err := mapstructure.Decode(input, &opts)
if err != nil { if err != nil {
t.Errorf("Did not successfully generate ImageFilterOptions from %v. Contains %v", input, opts) t.Errorf("Did not successfully generate ImageFilterOptions from %v.\nContains %v", input, opts)
} else {
t.Logf("Successfully generate ImageFilterOptions from %v.\nContains %v", input, opts)
} }
} }

View File

@ -77,7 +77,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
} }
if c.SourceImage == "" && c.SourceImageName == "" && c.SourceImageFilters.Filters == nil { if c.SourceImage == "" && c.SourceImageName == "" && c.SourceImageFilters.Filters == nil {
errs = append(errs, errors.New("Either a source_image, a source_image_name, or must be specified")) errs = append(errs, errors.New("Either a source_image, a source_image_name, or source_image_filter must be specified"))
} else if len(c.SourceImage) > 0 && len(c.SourceImageName) > 0 { } else if len(c.SourceImage) > 0 && len(c.SourceImageName) > 0 {
errs = append(errs, errors.New("Only a source_image or a source_image_name can be specified, not both.")) errs = append(errs, errors.New("Only a source_image or a source_image_name can be specified, not both."))
} }

View File

@ -18,7 +18,7 @@ type StepSourceImageInfo struct {
} }
type ImageFilterOptions struct { type ImageFilterOptions struct {
Filters map[string]string `mapstructure:"filters"` Filters map[string]interface{} `mapstructure:"filters"`
MostRecent bool `mapstructure:"most_recent"` MostRecent bool `mapstructure:"most_recent"`
} }
@ -28,7 +28,7 @@ func (s *StepSourceImageInfo) Run(_ context.Context, state multistep.StateBag) m
client, err := config.computeV2Client() client, err := config.computeV2Client()
// if an ID is provided we skip the filter since that will return a single image // if an ID is provided we skip the filter since that will return a single or no image
if s.SourceImage != "" { if s.SourceImage != "" {
state.Put("source_image", s.SourceImage) state.Put("source_image", s.SourceImage)
return multistep.ActionContinue return multistep.ActionContinue
@ -47,6 +47,12 @@ func (s *StepSourceImageInfo) Run(_ context.Context, state multistep.StateBag) m
} }
} }
// override the "name" provided in filters if "s.SourceImageName" was filled
if s.SourceImageName != "" {
params.Name = s.SourceImageName
}
// apply "most_recent" logic to the sort fields and allow OpenStack to return the latest qualified image
if s.ImageFilters.MostRecent { if s.ImageFilters.MostRecent {
applyMostRecent(params) applyMostRecent(params)
} }

View File

@ -184,11 +184,11 @@ builder.
``` ```
This selects the most recent production Ubuntu 16.04 shared to you by the given owner. This selects the most recent production Ubuntu 16.04 shared to you by the given owner.
NOTE: This will fail unless *exactly* one image is returned. In the above NOTE: This will fail unless *exactly* one image is returned, or `most_recent` is set to true.
example, `most_recent` will cause this to succeed by selecting the newest image. In the example, `most_recent` will cause this to succeed by selecting the newest image.
- `filters` (map of strings) - filters used to select a `source_image`. - `filters` (map of strings) - filters used to select a `source_image`.
NOTE: This will fail unless *exactly* one image is returned. NOTE: This will fail unless *exactly* one image is returned, or `most_recent` is set to true.
Any filter described in the docs for [ImageService](https://developer.openstack.org/api-ref/image/v2/) Any filter described in the docs for [ImageService](https://developer.openstack.org/api-ref/image/v2/)
is valid. is valid.