-
Notifications
You must be signed in to change notification settings - Fork 557
[glance]: handle various image struct member types #3159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package assert | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
) | ||
|
||
const errf = "unknown type for %s: %T (value: %v)" | ||
|
||
func Int(v any, name string) (int, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NumberOrString? |
||
switch t := v.(type) { | ||
case nil: | ||
return 0, nil | ||
case int: | ||
return t, nil | ||
case int64: | ||
return int(t), nil | ||
case float32: | ||
return int(t), nil | ||
case float64: | ||
return int(t), nil | ||
case string: | ||
return strconv.Atoi(t) | ||
} | ||
|
||
return 0, fmt.Errorf(errf, name, v, v) | ||
} | ||
|
||
func Int64(v any, name string) (int64, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you made NumberOrString generic on the return type you wouldn't need this function at all. type ConvertibleNumber interface { int | int64 | float32 | float64 } |
||
switch t := v.(type) { | ||
case nil: | ||
return 0, nil | ||
case int: | ||
return int64(t), nil | ||
case int64: | ||
return t, nil | ||
case float32: | ||
return int64(t), nil | ||
case float64: | ||
return int64(t), nil | ||
case string: | ||
return strconv.ParseInt(t, 10, 64) | ||
} | ||
|
||
return 0, fmt.Errorf(errf, name, v, v) | ||
} | ||
|
||
func String(v any, name string) (string, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this is necessary. |
||
switch t := v.(type) { | ||
case nil: | ||
return "", nil | ||
case string: | ||
return t, nil | ||
} | ||
|
||
return "", fmt.Errorf(errf, name, v, v) | ||
} | ||
|
||
func Bool(v any, name string) (bool, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BoolOrString? |
||
switch t := v.(type) { | ||
case nil: | ||
return false, nil | ||
case bool: | ||
return t, nil | ||
case string: | ||
return strconv.ParseBool(t) | ||
} | ||
|
||
return false, fmt.Errorf(errf, name, v, v) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,12 +2,11 @@ package images | |
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"reflect" | ||
"strings" | ||
"time" | ||
|
||
"github.com/gophercloud/gophercloud/v2" | ||
"github.com/gophercloud/gophercloud/v2/internal/assert" | ||
"github.com/gophercloud/gophercloud/v2/pagination" | ||
) | ||
|
||
|
@@ -17,7 +16,7 @@ type Image struct { | |
ID string `json:"id"` | ||
|
||
// Name is the human-readable display name for the image. | ||
Name string `json:"name"` | ||
Name string `json:"-"` | ||
|
||
// Status is the image status. It can be "queued" or "active" | ||
// See image/v2/images/type.go | ||
|
@@ -29,23 +28,23 @@ type Image struct { | |
|
||
// ContainerFormat is the format of the container. | ||
// Valid values are ami, ari, aki, bare, and ovf. | ||
ContainerFormat string `json:"container_format"` | ||
ContainerFormat string `json:"-"` | ||
|
||
// DiskFormat is the format of the disk. | ||
// If set, valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, | ||
// and iso. | ||
DiskFormat string `json:"disk_format"` | ||
DiskFormat string `json:"-"` | ||
|
||
// MinDiskGigabytes is the amount of disk space in GB that is required to | ||
// boot the image. | ||
MinDiskGigabytes int `json:"min_disk"` | ||
MinDiskGigabytes int `json:"-"` | ||
|
||
// MinRAMMegabytes [optional] is the amount of RAM in MB that is required to | ||
// boot the image. | ||
MinRAMMegabytes int `json:"min_ram"` | ||
MinRAMMegabytes int `json:"-"` | ||
|
||
// Owner is the tenant ID the image belongs to. | ||
Owner string `json:"owner"` | ||
Owner string `json:"-"` | ||
|
||
// Protected is whether the image is deletable or not. | ||
Protected bool `json:"protected"` | ||
|
@@ -54,10 +53,10 @@ type Image struct { | |
Visibility ImageVisibility `json:"visibility"` | ||
|
||
// Hidden is whether the image is listed in default image list or not. | ||
Hidden bool `json:"os_hidden"` | ||
Hidden bool `json:"-"` | ||
|
||
// Checksum is the checksum of the data that's associated with the image. | ||
Checksum string `json:"checksum"` | ||
Checksum string `json:"-"` | ||
|
||
// SizeBytes is the size of the data that's associated with the image. | ||
SizeBytes int64 `json:"-"` | ||
|
@@ -70,7 +69,7 @@ type Image struct { | |
|
||
// Properties is a set of key-value pairs, if any, that are associated with | ||
// the image. | ||
Properties map[string]any | ||
Properties map[string]any `json:"-"` | ||
|
||
// CreatedAt is the date when the image has been created. | ||
CreatedAt time.Time `json:"created_at"` | ||
|
@@ -88,7 +87,7 @@ type Image struct { | |
Schema string `json:"schema"` | ||
|
||
// VirtualSize is the virtual size of the image | ||
VirtualSize int64 `json:"virtual_size"` | ||
VirtualSize int64 `json:"-"` | ||
|
||
// OpenStackImageImportMethods is a slice listing the types of import | ||
// methods available in the cloud. | ||
|
@@ -102,7 +101,16 @@ func (r *Image) UnmarshalJSON(b []byte) error { | |
type tmp Image | ||
var s struct { | ||
tmp | ||
Name any `json:"name"` | ||
ContainerFormat any `json:"container_format"` | ||
DiskFormat any `json:"disk_format"` | ||
MinDiskGigabytes any `json:"min_disk"` | ||
MinRAMMegabytes any `json:"min_ram"` | ||
Owner any `json:"owner"` | ||
Hidden any `json:"os_hidden"` | ||
Checksum any `json:"checksum"` | ||
SizeBytes any `json:"size"` | ||
VirtualSize any `json:"virtual_size"` | ||
OpenStackImageImportMethods string `json:"openstack-image-import-methods"` | ||
OpenStackImageStoreIDs string `json:"openstack-image-store-ids"` | ||
} | ||
|
@@ -112,15 +120,48 @@ func (r *Image) UnmarshalJSON(b []byte) error { | |
} | ||
*r = Image(s.tmp) | ||
|
||
switch t := s.SizeBytes.(type) { | ||
case nil: | ||
r.SizeBytes = 0 | ||
case float32: | ||
r.SizeBytes = int64(t) | ||
case float64: | ||
r.SizeBytes = int64(t) | ||
default: | ||
return fmt.Errorf("Unknown type for SizeBytes: %v (value: %v)", reflect.TypeOf(t), t) | ||
// some of the values may be nil, or bool as a string format, or int as | ||
// a string format so we need to assert them to the correct type using | ||
// the assert package | ||
r.Name, err = assert.String(s.Name, "Name") | ||
if err != nil { | ||
return err | ||
} | ||
Comment on lines
+126
to
+129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this necessary? What's the difference between this and regular json unmarshalling with Ditto all other uses of String below. |
||
r.ContainerFormat, err = assert.String(s.ContainerFormat, "ContainerFormat") | ||
if err != nil { | ||
return err | ||
} | ||
r.DiskFormat, err = assert.String(s.DiskFormat, "DiskFormat") | ||
if err != nil { | ||
return err | ||
} | ||
r.MinDiskGigabytes, err = assert.Int(s.MinDiskGigabytes, "MinDiskGigabytes") | ||
if err != nil { | ||
return err | ||
} | ||
r.MinRAMMegabytes, err = assert.Int(s.MinRAMMegabytes, "MinRAMMegabytes") | ||
if err != nil { | ||
return err | ||
} | ||
r.Owner, err = assert.String(s.Owner, "Owner") | ||
if err != nil { | ||
return err | ||
} | ||
r.Hidden, err = assert.Bool(s.Hidden, "Hidden") | ||
if err != nil { | ||
return err | ||
} | ||
r.Checksum, err = assert.String(s.Checksum, "Checksum") | ||
if err != nil { | ||
return err | ||
} | ||
r.SizeBytes, err = assert.Int64(s.SizeBytes, "SizeBytes") | ||
if err != nil { | ||
return err | ||
} | ||
r.VirtualSize, err = assert.Int64(s.VirtualSize, "VirtualSize") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Bundle all other fields into Properties | ||
|
@@ -131,10 +172,22 @@ func (r *Image) UnmarshalJSON(b []byte) error { | |
} | ||
if resultMap, ok := result.(map[string]any); ok { | ||
delete(resultMap, "self") | ||
delete(resultMap, "name") | ||
delete(resultMap, "container_format") | ||
delete(resultMap, "disk_format") | ||
delete(resultMap, "min_disk") | ||
delete(resultMap, "min_ram") | ||
delete(resultMap, "owner") | ||
delete(resultMap, "os_hidden") | ||
delete(resultMap, "checksum") | ||
delete(resultMap, "size") | ||
delete(resultMap, "virtual_size") | ||
delete(resultMap, "openstack-image-import-methods") | ||
delete(resultMap, "openstack-image-store-ids") | ||
r.Properties = gophercloud.RemainingKeys(Image{}, resultMap) | ||
if m, ok := resultMap["properties"]; ok { | ||
r.Properties["properties"] = m | ||
} | ||
} | ||
|
||
if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageImportMethods), splitFunc); len(v) > 0 { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,12 +34,12 @@ func HandleImageListSuccessfully(t *testing.T) { | |
"updated_at": "2015-07-15T11:43:35Z", | ||
"visibility": "public", | ||
"self": "/v2/images/07aa21a9-fa1a-430e-9a33-185be5982431", | ||
"min_disk": 0, | ||
"protected": false, | ||
"min_disk": 4e-06, | ||
"protected": null, | ||
"id": "07aa21a9-fa1a-430e-9a33-185be5982431", | ||
"size": 25165824, | ||
"file": "/v2/images/07aa21a9-fa1a-430e-9a33-185be5982431/file", | ||
"checksum": "eb9139e4942121f22bbc2afc0400b2a4", | ||
"checksum": null, | ||
"owner": "cba624273b8344e59dd1fd18685183b0", | ||
"virtual_size": null, | ||
"min_ram": 0, | ||
|
@@ -60,12 +60,12 @@ func HandleImageListSuccessfully(t *testing.T) { | |
"updated_at": "2015-07-15T11:43:32Z", | ||
"visibility": "public", | ||
"self": "/v2/images/8c64f48a-45a3-4eaa-adff-a8106b6c005b", | ||
"min_disk": 0, | ||
"min_disk": null, | ||
"protected": false, | ||
"id": "8c64f48a-45a3-4eaa-adff-a8106b6c005b", | ||
"file": "/v2/images/8c64f48a-45a3-4eaa-adff-a8106b6c005b/file", | ||
"checksum": "be575a2b939972276ef675752936977f", | ||
"owner": "cba624273b8344e59dd1fd18685183b0", | ||
"owner": null, | ||
"virtual_size": null, | ||
"min_ram": 0, | ||
"schema": "/v2/schemas/image", | ||
|
@@ -271,6 +271,44 @@ func HandleImageGetSuccessfully(t *testing.T) { | |
"self": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27", | ||
"min_disk": 0, | ||
"protected": false, | ||
"properties": "{'hypervisor_type': 'qemu', 'architecture': 'x86_64'}", | ||
"id": "1bea47ed-f6a9-463b-b423-14b9cca9ad27", | ||
"file": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27/file", | ||
"checksum": "64d7c1cd2b6f60c92c14662941cb7913", | ||
"owner": "5ef70662f8b34079a6eddb8da9d75fe8", | ||
"size": 13167616, | ||
"min_ram": 0, | ||
"schema": "/v2/schemas/image", | ||
"virtual_size": null, | ||
"hw_disk_bus": "scsi", | ||
"hw_disk_bus_model": "virtio-scsi", | ||
"hw_scsi_model": "virtio-scsi" | ||
}`) | ||
}) | ||
} | ||
|
||
// HandleImageGetSuccessfullyStringBool test setup | ||
func HandleImageGetSuccessfullyStringBool(t *testing.T) { | ||
th.Mux.HandleFunc("/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27", func(w http.ResponseWriter, r *http.Request) { | ||
th.TestMethod(t, r, "GET") | ||
th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) | ||
|
||
w.Header().Add("Content-Type", "application/json") | ||
w.WriteHeader(http.StatusOK) | ||
fmt.Fprintf(w, `{ | ||
"status": "active", | ||
"name": "cirros-0.3.2-x86_64-disk", | ||
"tags": [], | ||
"container_format": "bare", | ||
"created_at": "2014-05-05T17:15:10Z", | ||
"disk_format": "qcow2", | ||
"updated_at": "2014-05-05T17:15:11Z", | ||
"visibility": "public", | ||
"os_hidden": "False", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could do with a note about what we're simulating here also... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
"self": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27", | ||
"min_disk": 0, | ||
"protected": false, | ||
"properties": "{'hypervisor_type': 'qemu', 'architecture': 'x86_64'}", | ||
"id": "1bea47ed-f6a9-463b-b423-14b9cca9ad27", | ||
"file": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27/file", | ||
"checksum": "64d7c1cd2b6f60c92c14662941cb7913", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These aren't assertions. These are format conversions.
jsonhelpers
maybe?