mirror of https://github.com/hashicorp/packer
parent
3eb4151599
commit
3b49caaf40
@ -1,120 +0,0 @@
|
|||||||
package openstack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
|
||||||
"github.com/hashicorp/packer/packer"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
mostRecentSort = "created_at:desc"
|
|
||||||
)
|
|
||||||
|
|
||||||
var validFields = map[string]string{
|
|
||||||
"Name": "name",
|
|
||||||
"Visibility": "visibility",
|
|
||||||
"Owner": "owner",
|
|
||||||
"Tags": "tags",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the specific ImageVisibility using the exported const from images
|
|
||||||
func getImageVisibility(s string) (images.ImageVisibility, error) {
|
|
||||||
visibilities := [...]images.ImageVisibility{
|
|
||||||
images.ImageVisibilityPublic,
|
|
||||||
images.ImageVisibilityPrivate,
|
|
||||||
images.ImageVisibilityCommunity,
|
|
||||||
images.ImageVisibilityShared,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, visibility := range visibilities {
|
|
||||||
if string(visibility) == s {
|
|
||||||
return visibility, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var nilVisibility images.ImageVisibility
|
|
||||||
return nilVisibility, fmt.Errorf("No valid ImageVisibility found for %s", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allows construction of all supported fields from ListOpts
|
|
||||||
// The `input` map will be modified but is not reused further in the builder
|
|
||||||
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))
|
|
||||||
multiErr := packer.MultiError{}
|
|
||||||
|
|
||||||
for i := 0; i < metaOpts.Type().NumField(); i++ {
|
|
||||||
vField := metaOpts.Field(i)
|
|
||||||
fieldName := metaOpts.Type().Field(i).Name
|
|
||||||
|
|
||||||
// check the valid fields map and whether we can set this field
|
|
||||||
if key, exists := validFields[fieldName]; exists {
|
|
||||||
|
|
||||||
// check that this key was provided by the user, then set the field and have compatible types
|
|
||||||
if val, exists := input[key]; exists {
|
|
||||||
|
|
||||||
// non-settable field
|
|
||||||
if !vField.CanSet() {
|
|
||||||
multiErr.Errors = append(multiErr.Errors, fmt.Errorf("Unsettable field: %s", fieldName))
|
|
||||||
|
|
||||||
// remove key from input filters so we can go over them after
|
|
||||||
delete(input, key)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch key {
|
|
||||||
case "owner", "name", "tags":
|
|
||||||
if valType := reflect.TypeOf(val); valType != vField.Type() {
|
|
||||||
multiErr.Errors = append(multiErr.Errors,
|
|
||||||
fmt.Errorf("Invalid type '%v' for field %s (%s)",
|
|
||||||
valType,
|
|
||||||
key,
|
|
||||||
fieldName,
|
|
||||||
))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
vField.Set(reflect.ValueOf(val))
|
|
||||||
|
|
||||||
case "visibility":
|
|
||||||
visibility, err := getImageVisibility(val.(string))
|
|
||||||
if err != nil {
|
|
||||||
multiErr.Errors = append(multiErr.Errors, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
vField.Set(reflect.ValueOf(visibility))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove key from input filters so we can go over them after
|
|
||||||
delete(input, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// error any invalid filters
|
|
||||||
for key, value := range input {
|
|
||||||
multiErr.Errors = append(multiErr.Errors, fmt.Errorf("Invalid filter: %s: %v (type: %v)",
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
reflect.TypeOf(value),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set defaults for status and member_status
|
|
||||||
listOpts.Status = images.ImageStatusActive
|
|
||||||
listOpts.MemberStatus = images.ImageMemberStatusAccepted
|
|
||||||
|
|
||||||
return &multiErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply most recent filtering logic to ListOpts where user has filled fields.
|
|
||||||
// See https://developer.openstack.org/api-ref/image/v2/
|
|
||||||
func applyMostRecent(listOpts *images.ListOpts) {
|
|
||||||
// Sort isn't supported through our API so there should be no existing values.
|
|
||||||
// Overwriting ListOpts.Sort is okay.
|
|
||||||
listOpts.Sort = mostRecentSort
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
package openstack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestImageFilterOptionsDecode(t *testing.T) {
|
|
||||||
opts := ImageFilterOptions{}
|
|
||||||
input := map[string]interface{}{
|
|
||||||
"most_recent": true,
|
|
||||||
"filters": map[string]interface{}{
|
|
||||||
"visibility": "protected",
|
|
||||||
"tag": []string{"prod", "ready"},
|
|
||||||
"name": "ubuntu 16.04",
|
|
||||||
"owner": "tcarrio",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
err := mapstructure.Decode(input, &opts)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Did not successfully generate ImageFilterOptions from %v.\nContains %v", input, opts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This test case confirms that only allowed fields will be set to values
|
|
||||||
// The checked values are non-nil for their target type
|
|
||||||
func TestBuildImageFilter(t *testing.T) {
|
|
||||||
testOpts := images.ListOpts{}
|
|
||||||
|
|
||||||
filters := map[string]interface{}{
|
|
||||||
"limit": "3",
|
|
||||||
"name": "Ubuntu 16.04",
|
|
||||||
"visibility": "public",
|
|
||||||
"status": "active",
|
|
||||||
"size_min": "25",
|
|
||||||
"sort": "created_at:desc",
|
|
||||||
"tags": []string{"prod", "ready"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy of original filters to pass to build function
|
|
||||||
passedFilters := make(map[string]interface{})
|
|
||||||
for k, v := range filters {
|
|
||||||
passedFilters[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
buildImageFilters(passedFilters, &testOpts)
|
|
||||||
|
|
||||||
if testOpts.Limit != 0 {
|
|
||||||
t.Errorf("Limit was parsed: %d", testOpts.Limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if testOpts.Name != filters["name"] {
|
|
||||||
t.Errorf("Name did not parse correctly: %s", testOpts.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if testOpts.Visibility != images.ImageVisibilityPublic {
|
|
||||||
t.Errorf("Visibility did not parse correctly: %v", testOpts.Visibility)
|
|
||||||
}
|
|
||||||
|
|
||||||
if testOpts.Status != images.ImageStatusActive {
|
|
||||||
t.Errorf("Image status did not parse correctly: %s", testOpts.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
if testOpts.SizeMin != 0 {
|
|
||||||
t.Errorf("Size min was parsed: %d", testOpts.SizeMin)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(testOpts.Sort) > 0 {
|
|
||||||
t.Errorf("Sort was parsed: %s", testOpts.Sort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This test case confirms that invalid filter input are caught and do not result in a panic
|
|
||||||
func TestInvalidFilterInput(t *testing.T) {
|
|
||||||
|
|
||||||
testOpts := images.ListOpts{}
|
|
||||||
|
|
||||||
filters := map[string]interface{}{
|
|
||||||
"tags": "prod", // supposed to be a []string
|
|
||||||
"owner": 12345, // supposed to be a string
|
|
||||||
"invalid_field": 0, // not a valid field in ListOpts
|
|
||||||
}
|
|
||||||
|
|
||||||
numFields := len(filters)
|
|
||||||
|
|
||||||
multiErr := buildImageFilters(filters, &testOpts)
|
|
||||||
if len(multiErr.Errors) != numFields {
|
|
||||||
t.Errorf("Failed to catch all %d invalid types/fields in filters", numFields)
|
|
||||||
for _, err := range multiErr.Errors {
|
|
||||||
t.Log(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApplyMostRecent(t *testing.T) {
|
|
||||||
testSortOpts := images.ListOpts{
|
|
||||||
Name: "RHEL 7.0",
|
|
||||||
Tags: []string{"prod", "ready"},
|
|
||||||
}
|
|
||||||
|
|
||||||
applyMostRecent(&testSortOpts)
|
|
||||||
|
|
||||||
if testSortOpts.Sort != "created_at:desc" {
|
|
||||||
t.Errorf("Error applying most recent filter: sort")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in new issue