From 58f8f088e2ed3c6fd44706f55bba8f362fa61c97 Mon Sep 17 00:00:00 2001 From: Olaf Seibert Date: Tue, 30 Apr 2019 16:34:40 +0200 Subject: [PATCH] Add image filtering on properties. Initial commit with debugging info. --- builder/openstack/builder.go | 1 + builder/openstack/run_config.go | 13 ++--- builder/openstack/run_config_test.go | 2 + builder/openstack/step_source_image_info.go | 52 ++++++++++++++++--- .../source/docs/builders/openstack.html.md | 8 ++- 5 files changed, 62 insertions(+), 14 deletions(-) diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index 24921656c..661913dd0 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -97,6 +97,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack SourceImageName: b.config.RunConfig.SourceImageName, SourceImageOpts: b.config.RunConfig.sourceImageOpts, SourceMostRecent: b.config.SourceImageFilters.MostRecent, + SourceProperties: b.config.SourceImageFilters.Filters.Properties, }, &StepCreateVolume{ UseBlockStorageVolume: b.config.UseBlockStorageVolume, diff --git a/builder/openstack/run_config.go b/builder/openstack/run_config.go index 9077a5e7d..f1a1877a4 100644 --- a/builder/openstack/run_config.go +++ b/builder/openstack/run_config.go @@ -48,7 +48,7 @@ type RunConfig struct { OpenstackProvider string `mapstructure:"openstack_provider"` UseFloatingIp bool `mapstructure:"use_floating_ip"` - sourceImageOpts images.ListOpts + sourceImageOpts images.ListOpts // derived from .SourceImageFilters.Filters ImageFilterOptions } type ImageFilter struct { @@ -57,14 +57,15 @@ type ImageFilter struct { } type ImageFilterOptions struct { - Name string `mapstructure:"name"` - Owner string `mapstructure:"owner"` - Tags []string `mapstructure:"tags"` - Visibility string `mapstructure:"visibility"` + Name string `mapstructure:"name"` + Owner string `mapstructure:"owner"` + Tags []string `mapstructure:"tags"` + Visibility string `mapstructure:"visibility"` + Properties map[string]string `mapstructure:"properties"` } func (f *ImageFilterOptions) Empty() bool { - return f.Name == "" && f.Owner == "" && len(f.Tags) == 0 && f.Visibility == "" + return f.Name == "" && f.Owner == "" && len(f.Tags) == 0 && f.Visibility == "" && len(f.Properties) == 0 } func (f *ImageFilterOptions) Build() (*images.ListOpts, error) { diff --git a/builder/openstack/run_config_test.go b/builder/openstack/run_config_test.go index f660a4e82..fd535496f 100644 --- a/builder/openstack/run_config_test.go +++ b/builder/openstack/run_config_test.go @@ -139,6 +139,7 @@ func TestBuildImageFilter(t *testing.T) { Visibility: "public", Owner: "1234567890", Tags: []string{"prod", "ready"}, + Properties: map[string]string{"os_distro": "ubuntu", "os_version": "16.04"}, } listOpts, err := filters.Build() @@ -198,6 +199,7 @@ func TestImageFiltersEmpty(t *testing.T) { Visibility: "public", Owner: "1234567890", Tags: []string{"prod", "ready"}, + Properties: map[string]string{"os_distro": "ubuntu", "os_version": "16.04"}, } if filledFilters.Empty() { diff --git a/builder/openstack/step_source_image_info.go b/builder/openstack/step_source_image_info.go index 3ba373941..2be0f3038 100644 --- a/builder/openstack/step_source_image_info.go +++ b/builder/openstack/step_source_image_info.go @@ -16,6 +16,17 @@ type StepSourceImageInfo struct { SourceImageName string SourceImageOpts images.ListOpts SourceMostRecent bool + SourceProperties map[string]string +} + +func PropertiesSatisfied(image *images.Image, props *map[string]string) bool { + for key, value := range *props { + if image.Properties[key] != value { + return false + } + } + + return true } func (s *StepSourceImageInfo) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -36,25 +47,52 @@ func (s *StepSourceImageInfo) Run(ctx context.Context, state multistep.StateBag) } } - log.Printf("Using Image Filters %v", s.SourceImageOpts) + log.Printf("Using Image Filters %+v", s.SourceImageOpts) image := &images.Image{} err = images.List(client, s.SourceImageOpts).EachPage(func(page pagination.Page) (bool, error) { - i, err := images.ExtractImages(page) + imgs, err := images.ExtractImages(page) if err != nil { return false, err } + ui.Message(fmt.Sprintf("Resulting images: %d", len(imgs))) + + count := 0 + first := -1 + + for index, img := range imgs { + ui.Message(fmt.Sprintf("index +%v, image %+v", index, img)) + ui.Message(fmt.Sprintf("Metadata %+v", img.Metadata)) + ui.Message(fmt.Sprintf("Properties %+v", img.Properties)) + + // Check if all Properties are satisfied + if PropertiesSatisfied(&img, &s.SourceProperties) { + ui.Message(fmt.Sprintf("Matched properties %+v", s.SourceProperties)) + count++ + if first < 0 { + first = index + } + // Don't iterate over entries we will never use. + if count > 1 { + break + } + } else { + ui.Message(fmt.Sprintf("FAILED to match properties %+v", s.SourceProperties)) + } + } - switch len(i) { + switch count { + case 0: + return true, nil case 1: - *image = i[0] + *image = imgs[first] return false, nil default: if s.SourceMostRecent { - *image = i[0] + *image = imgs[first] return false, nil } return false, fmt.Errorf( - "Your query returned more than one result. Please try a more specific search, or set most_recent to true. Search filters: %v", + "Your query returned more than one result. Please try a more specific search, or set most_recent to true. Search filters: %+v", s.SourceImageOpts) } }) @@ -67,7 +105,7 @@ func (s *StepSourceImageInfo) Run(ctx context.Context, state multistep.StateBag) } if image.ID == "" { - err := fmt.Errorf("No image was found matching filters: %v", s.SourceImageOpts) + err := fmt.Errorf("No image was found matching filters: %+v", s.SourceImageOpts) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt diff --git a/website/source/docs/builders/openstack.html.md b/website/source/docs/builders/openstack.html.md index 17f6a624d..a11780db0 100644 --- a/website/source/docs/builders/openstack.html.md +++ b/website/source/docs/builders/openstack.html.md @@ -202,7 +202,10 @@ builder. "name": "ubuntu-16.04", "visibility": "protected", "owner": "d1a588cf4b0743344508dc145649372d1", - "tags": ["prod", "ready"] + "tags": ["prod", "ready"], + "properties": { + "os_distro": "ubuntu" + } }, "most_recent": true } @@ -229,6 +232,9 @@ builder. - visibility (string) + - properties (map of strings to strings) (fields that can be set + with `openstack image set --property key=value`) + - `most_recent` (boolean) - Selects the newest created image when true. This is most useful for selecting a daily distro build.