Skip to content
This repository has been archived by the owner on Aug 1, 2023. It is now read-only.

Commit

Permalink
imageservice: Simplify Image extraction
Browse files Browse the repository at this point in the history
We can just call Decode() on it if we assume glance sets
optional values properly.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
  • Loading branch information
Samuel Ortiz committed Jun 21, 2016
1 parent 836043d commit 1836997
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 278 deletions.
18 changes: 9 additions & 9 deletions openstack/imageservice/v2/images/fixtures.go
Expand Up @@ -186,9 +186,9 @@ func HandleImageCreationSuccessfully(t *testing.T) {
"owner": "b4eedccc6fb74fa8a7ad6b08382b852b",
"min_ram": 0,
"schema": "/v2/schemas/image",
"size": "None",
"checksum": "None",
"virtual_size": "None"
"size": 0,
"checksum": "",
"virtual_size": 0
}`)
})
}
Expand Down Expand Up @@ -320,12 +320,12 @@ func HandleImageUpdateSuccessfully(t *testing.T) {
"self": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea",
"file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file",
"schema": "/v2/schemas/image",
"owner": "None",
"min_ram": "None",
"min_disk": "None",
"disk_format": "None",
"virtual_size": "None",
"container_format": "None"
"owner": "",
"min_ram": 0,
"min_disk": 0,
"disk_format": "",
"virtual_size": 0,
"container_format": ""
}`)
})
}
Expand Down
12 changes: 0 additions & 12 deletions openstack/imageservice/v2/images/requests_test.go
Expand Up @@ -85,9 +85,6 @@ func TestCreateImage(t *testing.T) {
Owner: owner,

Visibility: ImageVisibilityPrivate,

Metadata: make(map[string]string),
Properties: make(map[string]string),
}

th.AssertDeepEquals(t, &expectedImage, actualImage)
Expand Down Expand Up @@ -132,9 +129,6 @@ func TestCreateImageNulls(t *testing.T) {
Owner: owner,

Visibility: ImageVisibilityPrivate,

Metadata: make(map[string]string),
Properties: make(map[string]string),
}

th.AssertDeepEquals(t, &expectedImage, actualImage)
Expand Down Expand Up @@ -178,9 +172,6 @@ func TestGetImage(t *testing.T) {

Checksum: checksum,
SizeBytes: sizeBytes,

Metadata: make(map[string]string),
Properties: make(map[string]string),
}

th.AssertDeepEquals(t, &expectedImage, actualImage)
Expand Down Expand Up @@ -232,9 +223,6 @@ func TestUpdateImage(t *testing.T) {

DiskFormat: "",
ContainerFormat: "",

Metadata: make(map[string]string),
Properties: make(map[string]string),
}

th.AssertDeepEquals(t, &expectedImage, actualImage)
Expand Down
274 changes: 17 additions & 257 deletions openstack/imageservice/v2/images/results.go
Expand Up @@ -34,33 +34,33 @@ type Image struct {

// DiskFormat is the format of the disk.
// If set, valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, and iso.
DiskFormat string `mapstructure:"disk_format"`
DiskFormat string `mapstructure:"disk_format"`

// MinDiskGigabytes is the amount of disk space in GB that is required to boot the image.
MinDiskGigabytes int `mapstructure:"min_disk"`

// MinRAMMegabytes [optional] is the amount of RAM in MB that is required to boot the image.
MinRAMMegabytes int `mapstructure:"min_ram"`
MinRAMMegabytes int `mapstructure:"min_ram"`

// Owner is the tenant the image belongs to.
Owner string

// Protected is whether the image is deletable or not.
Protected bool
Protected bool

// Visibility defines who can see/use the image.
Visibility ImageVisibility

// Checksum is the checksum of the data that's associated with the image
Checksum string
Checksum string

// SizeBytes is the size of the data that's associated with the image.
SizeBytes int `mapstructure:"size"`

// Metadata is a set of metadata associated with the image.
// Image metadata allow for meaningfully define the image properties
// and tags. See http://docs.openstack.org/developer/glance/metadefs-concepts.html.
Metadata map[string]string
Metadata map[string]string

// Properties is a set of key-value pairs, if any, that are associated with the image.
Properties map[string]string
Expand All @@ -71,258 +71,12 @@ type CreateResult struct {
gophercloud.Result
}

func asBool(any interface{}) (bool, error) {
if b, ok := any.(bool); ok {
return b, nil
}
return false, fmt.Errorf("expected bool value, but found: %#v", any)
}

func asInt(any interface{}) (int, error) {
// FIXME integers decoded as float64s
if f, ok := any.(float64); ok {
i := int(f)
return i, nil
}
return 0, fmt.Errorf("expected int value, but found: %#v", any)
}

func asString(any interface{}, key string) (string, error) {
if str, ok := any.(string); ok {
return str, nil
}
return "", fmt.Errorf("expected string value for key '%s', but found: %#v", key, any)
}

func asNoneableString(any interface{}, key string) (string, error) {
// JSON null values could be also returned according to behaviour https://bugs.launchpad.net/glance/+bug/1481512
if any == nil {
return "", nil
}
if str, ok := any.(string); ok {
if str == "None" || &str == nil {
return "", nil
}
return str, nil
}
return "", fmt.Errorf("expected string value for key '%s', but found: %#v", key, any)
}

func asNoneableInteger(any interface{}, key string) (int, error) {
// FIXME problem here is that provider_client.go uses: json.NewDecoder(resp.Body).Decode(options.JSONResponse)
// which apparently converts integers in JSON to float64 values
// JSON null values could be also returned according to behaviour https://bugs.launchpad.net/glance/+bug/1481512
if any == nil {
return 0, nil
}
if f, ok := any.(float64); ok {
i := int(f)
return i, nil
} else if s, ok := any.(string); ok {
if s == "None" {
return 0, nil
}
return 0, fmt.Errorf("expected \"None\" or integer value for key '%s', but found unexpected string: \"%s\"", key, s)
}
return 0, fmt.Errorf("expected \"None\" or integer value for key '%s', but found: %T(%#v)", key, any, any)
}

func asMapStringString(any interface{}) (map[string]string, error) {
if mss, ok := any.(map[string]string); ok {
return mss, nil
}
return nil, fmt.Errorf("expected map[string]string, but found: %#v", any)
}

func extractBoolAtKey(m map[string]interface{}, k string) (bool, error) {
if any, ok := m[k]; ok {
return asBool(any)
}
return false, fmt.Errorf("expected key \"%s\" in map, but this key is not present", k)
}

func extractIntAtKey(m map[string]interface{}, k string) (int, error) {
if any, ok := m[k]; ok {
return asInt(any)
}
return 0, fmt.Errorf("expected key \"%s\" in map, but this key is not present", k)
}

func extractStringAtKey(m map[string]interface{}, k string) (string, error) {
if any, ok := m[k]; ok {
return asString(any, k)
}
return "", fmt.Errorf("expected key \"%s\" in map, but this key is not present", k)
}

func extractNoneableStringAtKey(m map[string]interface{}, k string) (string, error) {
if any, ok := m[k]; ok {
return asNoneableString(any, k)
}
return "", fmt.Errorf("expected key \"%s\" in map, but this key is not present", k)
}

func extractNoneableIntegerAtKey(m map[string]interface{}, k string) (int, error) {
if any, ok := m[k]; ok {
return asNoneableInteger(any, k)
}
return 0, fmt.Errorf("expected key \"%s\" in map, but this key is not present", k)
}

func extractStringSliceAtKey(m map[string]interface{}, key string) ([]string, error) {
if any, ok := m[key]; ok {
if slice, ok := any.([]interface{}); ok {
res := make([]string, len(slice))
for k, v := range slice {
var err error
if res[k], err = asString(v, key); err != nil {
return nil, err
}
}
return res, nil
}
return nil, fmt.Errorf("expected slice as \"%s\" value, but found: %#v", key, any)
}
return nil, fmt.Errorf("expected key \"%s\" in map, but this key is not present", key)
}

func stringToImageStatus(s string) (ImageStatus, error) {
if s == "queued" {
return ImageStatusQueued, nil
} else if s == "active" {
return ImageStatusActive, nil
} else {
return "", fmt.Errorf("expected \"queued\" or \"active\" as image status, but found: \"%s\"", s)
}
}

func extractImageStatusAtKey(m map[string]interface{}, k string) (ImageStatus, error) {
if any, ok := m[k]; ok {
if str, ok := any.(string); ok {
return stringToImageStatus(str)
}
return "", fmt.Errorf("expected string as \"%s\" value, but found: %#v", k, any)
}
return "", fmt.Errorf("expected key \"%s\" in map, but this key is not present", k)

}

func stringToImageVisibility(s string) (ImageVisibility, error) {
if s == "public" {
return ImageVisibilityPublic, nil
} else if s == "private" {
return ImageVisibilityPrivate, nil
} else {
return "", fmt.Errorf("expected \"public\" or \"private\" as image status, but found: \"%s\"", s)
}
}

func extractImageVisibilityAtKey(m map[string]interface{}, k string) (ImageVisibility, error) {
if any, ok := m[k]; ok {
if str, ok := any.(string); ok {
return stringToImageVisibility(str)
}
return "", fmt.Errorf("expected string as \"%s\" value, but found: %#v", k, any)
}
return "", fmt.Errorf("expected key \"%s\" in map, but this key is not present", k)
}

func extractBoolAtKeyOptional(m map[string]interface{}, k string, ifMissing bool) (bool, error) {
if any, ok := m[k]; ok {
return asBool(any)
}
return ifMissing, nil

}

func extractMapStringStringAtKeyOptional(m map[string]interface{}, k string, ifMissing map[string]string) (map[string]string, error) {
if any, ok := m[k]; ok {
return asMapStringString(any)
}
return ifMissing, nil
}

func extractImage(res gophercloud.Result) (*Image, error) {
if res.Err != nil {
return nil, res.Err
}

body, ok := res.Body.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("expected map as result body, but found: %#v", res.Body)
}

var image Image

var err error

if image.ID, err = extractStringAtKey(body, "id"); err != nil {
return nil, err
}

if image.Name, err = extractStringAtKey(body, "name"); err != nil {
return nil, err
}

if image.Status, err = extractImageStatusAtKey(body, "status"); err != nil {
return nil, err
}

if image.Tags, err = extractStringSliceAtKey(body, "tags"); err != nil {
return nil, err
}

if image.ContainerFormat, err = extractNoneableStringAtKey(body, "container_format"); err != nil {
return nil, err
}

if image.DiskFormat, err = extractNoneableStringAtKey(body, "disk_format"); err != nil {
return nil, err
}

if image.MinDiskGigabytes, err = extractNoneableIntegerAtKey(body, "min_disk"); err != nil {
return nil, err
}

if image.MinRAMMegabytes, err = extractNoneableIntegerAtKey(body, "min_ram"); err != nil {
return nil, err
}

if image.Owner, err = extractNoneableStringAtKey(body, "owner"); err != nil {
return nil, err
}

// FIXME should this key actually be optional? Is a missing key equivalent to "protected": false ?
if image.Protected, err = extractBoolAtKeyOptional(body, "protected", false); err != nil {
return nil, err
}

if image.Visibility, err = extractImageVisibilityAtKey(body, "visibility"); err != nil {
return nil, err
}

if image.Checksum, err = extractNoneableStringAtKey(body, "checksum"); err != nil {
return nil, err
}

if image.SizeBytes, err = extractNoneableIntegerAtKey(body, "size"); err != nil {
return nil, err
}

if image.Metadata, err = extractMapStringStringAtKeyOptional(body, "metadata", make(map[string]string)); err != nil {
return nil, err
}

if image.Properties, err = extractMapStringStringAtKeyOptional(body, "properties", make(map[string]string)); err != nil {
return nil, err
}

return &image, nil
}

// Extract build CreateResults from imput Image
func (c CreateResult) Extract() (*Image, error) {
return extractImage(c.Result)
var image *Image

err := mapstructure.Decode(c.Result.Body, &image)
return image, err
}

//DeleteResult model
Expand All @@ -337,7 +91,10 @@ type GetResult struct {

// Extract builds GetResult
func (c GetResult) Extract() (*Image, error) {
return extractImage(c.Result)
var image *Image

err := mapstructure.Decode(c.Result.Body, &image)
return image, err
}

// UpdateResult model
Expand All @@ -347,7 +104,10 @@ type UpdateResult struct {

// Extract builds UpdateResult
func (u UpdateResult) Extract() (*Image, error) {
return extractImage(u.Result)
var image *Image

err := mapstructure.Decode(u.Result.Body, &image)
return image, err
}

// PutImageDataResult is model put image respose
Expand Down

0 comments on commit 1836997

Please sign in to comment.