From de9999ecb9ab61ccdfdc9278c4f753ff844f3e9e Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Mon, 16 Jul 2018 13:00:02 -0400 Subject: [PATCH] Updated tag to slice, docs, comments, only active images, source_image_name supercedes filter name --- builder/openstack/image_query.go | 35 +++++-------------- builder/openstack/image_query_test.go | 8 +++-- builder/openstack/run_config.go | 2 +- builder/openstack/step_source_image_info.go | 10 ++++-- .../source/docs/builders/openstack.html.md | 6 ++-- 5 files changed, 26 insertions(+), 35 deletions(-) diff --git a/builder/openstack/image_query.go b/builder/openstack/image_query.go index 2eb54eeb3..91cfb612e 100644 --- a/builder/openstack/image_query.go +++ b/builder/openstack/image_query.go @@ -59,19 +59,9 @@ func getImageVisibility(s string) (images.ImageVisibility, error) { // Retrieve the specific ImageVisibility using the exported const from images func getImageStatus(s string) (images.ImageStatus, error) { - statuses := [...]images.ImageStatus{ - images.ImageStatusActive, - images.ImageStatusDeactivated, - images.ImageStatusDeleted, - images.ImageStatusPendingDelete, - images.ImageStatusQueued, - images.ImageStatusSaving, - } - - for _, status := range statuses { - if string(status) == s { - return status, nil - } + activeStatus := images.ImageStatusActive + if string(activeStatus) == s { + return activeStatus, nil } 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 // 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 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 case reflect.Int64, reflect.Int: - iVal, err := strconv.Atoi(val) + iVal, err := strconv.Atoi(val.(string)) if err != nil { multierror.Append(err, multiErr.Errors...) continue @@ -120,7 +110,7 @@ func buildImageFilters(input map[string]string, listOpts *images.ListOpts) *pack vField.Set(reflect.ValueOf(val)) case reflect.TypeOf(images.ImageVisibility("")): - iv, err := getImageVisibility(val) + iv, err := getImageVisibility(val.(string)) if err != nil { multierror.Append(err, multiErr.Errors...) continue @@ -128,7 +118,7 @@ func buildImageFilters(input map[string]string, listOpts *images.ListOpts) *pack vField.Set(reflect.ValueOf(iv)) case reflect.TypeOf(images.ImageStatus("")): - is, err := getImageStatus(val) + is, err := getImageStatus(val.(string)) if err != nil { multierror.Append(err, multiErr.Errors...) continue @@ -138,14 +128,7 @@ func buildImageFilters(input map[string]string, listOpts *images.ListOpts) *pack // Generates slice of strings for Tags case reflect.Slice: - typeOfSlice := reflect.TypeOf(vField).Elem() - 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) + vField.Set(reflect.ValueOf(val)) default: multierror.Append( @@ -158,7 +141,7 @@ func buildImageFilters(input map[string]string, listOpts *images.ListOpts) *pack fieldName == reflect.TypeOf(listOpts.UpdatedAtQuery).Name() { // get ImageDateQuery from string and set to this field - query, err := dateToImageDateQuery(key, val) + query, err := dateToImageDateQuery(key, val.(string)) if err != nil { multierror.Append(err, multiErr.Errors...) continue diff --git a/builder/openstack/image_query_test.go b/builder/openstack/image_query_test.go index 1263669cf..d8b24feca 100644 --- a/builder/openstack/image_query_test.go +++ b/builder/openstack/image_query_test.go @@ -30,7 +30,7 @@ func TestGetImageFilter(t *testing.T) { func TestBuildImageFilter(t *testing.T) { testOpts := images.ListOpts{} - filters := map[string]string{ + filters := map[string]interface{}{ "limit": "3", "name": "Ubuntu 16.04", "visibility": "public", @@ -127,12 +127,14 @@ func TestImageFilterOptionsDecode(t *testing.T) { "most_recent": true, "filters": map[string]interface{}{ "visibility": "protected", - "tag": "prod", + "tag": []string{"prod","ready"}, "name": "ubuntu 16.04", }, } err := mapstructure.Decode(input, &opts) 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) } } diff --git a/builder/openstack/run_config.go b/builder/openstack/run_config.go index 1520369f5..b7d773743 100644 --- a/builder/openstack/run_config.go +++ b/builder/openstack/run_config.go @@ -77,7 +77,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { } 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 { errs = append(errs, errors.New("Only a source_image or a source_image_name can be specified, not both.")) } diff --git a/builder/openstack/step_source_image_info.go b/builder/openstack/step_source_image_info.go index 682bdacde..b5f6e20f9 100644 --- a/builder/openstack/step_source_image_info.go +++ b/builder/openstack/step_source_image_info.go @@ -18,7 +18,7 @@ type StepSourceImageInfo struct { } type ImageFilterOptions struct { - Filters map[string]string `mapstructure:"filters"` + Filters map[string]interface{} `mapstructure:"filters"` MostRecent bool `mapstructure:"most_recent"` } @@ -28,7 +28,7 @@ func (s *StepSourceImageInfo) Run(_ context.Context, state multistep.StateBag) m 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 != "" { state.Put("source_image", s.SourceImage) 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 { applyMostRecent(params) } diff --git a/website/source/docs/builders/openstack.html.md b/website/source/docs/builders/openstack.html.md index 4163d90b5..bdc70f34f 100644 --- a/website/source/docs/builders/openstack.html.md +++ b/website/source/docs/builders/openstack.html.md @@ -184,11 +184,11 @@ builder. ``` 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 - example, `most_recent` will cause this to succeed by selecting the newest image. + NOTE: This will fail unless *exactly* one image is returned, or `most_recent` is set to true. + 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`. - 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/) is valid.