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 }