From 71cd5eaae8765e278a9b3e5c4dd25ada65986f42 Mon Sep 17 00:00:00 2001 From: davidjumani Date: Mon, 29 Nov 2021 15:47:17 +0530 Subject: [PATCH] Support 4.x compatibility (#23) * handle type compatibilty for 4.x baseline * fix CSLong to be more permissive and add unit test * Rename CSLong to UUID Co-authored-by: Xavier MARCELET --- cloudstack/HostService.go | 18 ++++++++--------- cloudstack/HostService_test.go | 2 ++ cloudstack/cloudstack.go | 21 ++++++++++++++++++++ cloudstack/cloudstack_test.go | 32 +++++++++++++++++++++++++++++++ generate/generate.go | 35 ++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/cloudstack/HostService.go b/cloudstack/HostService.go index 3412c9a..b241677 100644 --- a/cloudstack/HostService.go +++ b/cloudstack/HostService.go @@ -356,7 +356,7 @@ type AddBaremetalHostResponse struct { Jobstatus int `json:"jobstatus"` Lastannotated string `json:"lastannotated"` Lastpinged string `json:"lastpinged"` - Managementserverid string `json:"managementserverid"` + Managementserverid UUID `json:"managementserverid"` Memoryallocated int64 `json:"memoryallocated"` Memoryallocatedbytes int64 `json:"memoryallocatedbytes"` Memoryallocatedpercentage string `json:"memoryallocatedpercentage"` @@ -789,7 +789,7 @@ type AddHostResponse struct { Jobstatus int `json:"jobstatus"` Lastannotated string `json:"lastannotated"` Lastpinged string `json:"lastpinged"` - Managementserverid string `json:"managementserverid"` + Managementserverid UUID `json:"managementserverid"` Memoryallocated int64 `json:"memoryallocated"` Memoryallocatedbytes int64 `json:"memoryallocatedbytes"` Memoryallocatedpercentage string `json:"memoryallocatedpercentage"` @@ -1032,7 +1032,7 @@ type CancelHostMaintenanceResponse struct { Jobstatus int `json:"jobstatus"` Lastannotated string `json:"lastannotated"` Lastpinged string `json:"lastpinged"` - Managementserverid string `json:"managementserverid"` + Managementserverid UUID `json:"managementserverid"` Memoryallocated int64 `json:"memoryallocated"` Memoryallocatedbytes int64 `json:"memoryallocatedbytes"` Memoryallocatedpercentage string `json:"memoryallocatedpercentage"` @@ -1830,7 +1830,7 @@ type FindHostsForMigrationResponse struct { JobID string `json:"jobid"` Jobstatus int `json:"jobstatus"` Lastpinged string `json:"lastpinged"` - Managementserverid int64 `json:"managementserverid"` + Managementserverid UUID `json:"managementserverid"` Memoryallocated string `json:"memoryallocated"` Memoryallocatedbytes int64 `json:"memoryallocatedbytes"` Memoryallocatedpercentage string `json:"memoryallocatedpercentage"` @@ -2645,7 +2645,7 @@ type Host struct { Jobstatus int `json:"jobstatus"` Lastannotated string `json:"lastannotated"` Lastpinged string `json:"lastpinged"` - Managementserverid string `json:"managementserverid"` + Managementserverid UUID `json:"managementserverid"` Memoryallocated int64 `json:"memoryallocated"` Memoryallocatedbytes int64 `json:"memoryallocatedbytes"` Memoryallocatedpercentage string `json:"memoryallocatedpercentage"` @@ -3167,7 +3167,7 @@ type HostsMetric struct { Jobstatus int `json:"jobstatus"` Lastannotated string `json:"lastannotated"` Lastpinged string `json:"lastpinged"` - Managementserverid string `json:"managementserverid"` + Managementserverid UUID `json:"managementserverid"` Memoryallocated int64 `json:"memoryallocated"` Memoryallocatedbytes int64 `json:"memoryallocatedbytes"` Memoryallocateddisablethreshold bool `json:"memoryallocateddisablethreshold"` @@ -3331,7 +3331,7 @@ type PrepareHostForMaintenanceResponse struct { Jobstatus int `json:"jobstatus"` Lastannotated string `json:"lastannotated"` Lastpinged string `json:"lastpinged"` - Managementserverid string `json:"managementserverid"` + Managementserverid UUID `json:"managementserverid"` Memoryallocated int64 `json:"memoryallocated"` Memoryallocatedbytes int64 `json:"memoryallocatedbytes"` Memoryallocatedpercentage string `json:"memoryallocatedpercentage"` @@ -3485,7 +3485,7 @@ type ReconnectHostResponse struct { Jobstatus int `json:"jobstatus"` Lastannotated string `json:"lastannotated"` Lastpinged string `json:"lastpinged"` - Managementserverid string `json:"managementserverid"` + Managementserverid UUID `json:"managementserverid"` Memoryallocated int64 `json:"memoryallocated"` Memoryallocatedbytes int64 `json:"memoryallocatedbytes"` Memoryallocatedpercentage string `json:"memoryallocatedpercentage"` @@ -3880,7 +3880,7 @@ type UpdateHostResponse struct { Jobstatus int `json:"jobstatus"` Lastannotated string `json:"lastannotated"` Lastpinged string `json:"lastpinged"` - Managementserverid string `json:"managementserverid"` + Managementserverid UUID `json:"managementserverid"` Memoryallocated int64 `json:"memoryallocated"` Memoryallocatedbytes int64 `json:"memoryallocatedbytes"` Memoryallocatedpercentage string `json:"memoryallocatedpercentage"` diff --git a/cloudstack/HostService_test.go b/cloudstack/HostService_test.go index 9d529f3..6d4acc2 100644 --- a/cloudstack/HostService_test.go +++ b/cloudstack/HostService_test.go @@ -88,6 +88,7 @@ func TestHostService_PrepareHostForMaintenance(t *testing.T) { resp, err := client.Host.PrepareHostForMaintenance(params) if err != nil { t.Errorf("Failed to prepare host for maintenance due to: %v", err) + return } if resp.Resourcestate != "PrepareForMaintenance" { t.Errorf("Failed to prepare host for maintenance") @@ -109,6 +110,7 @@ func TestHostService_CancelHostForMaintenance(t *testing.T) { resp, err := client.Host.CancelHostMaintenance(params) if err != nil { t.Errorf("Failed to cancel host for maintenance due to: %v", err) + return } if resp.Resourcestate != "Enabled" { t.Errorf("Failed to cancel host for maintenance") diff --git a/cloudstack/cloudstack.go b/cloudstack/cloudstack.go index 0b6896a..4933845 100644 --- a/cloudstack/cloudstack.go +++ b/cloudstack/cloudstack.go @@ -35,6 +35,7 @@ import ( "net/url" "regexp" "sort" + "strconv" "strings" "time" @@ -67,6 +68,26 @@ func (e *CSError) Error() error { return fmt.Errorf("CloudStack API error %d (CSExceptionErrorCode: %d): %s", e.ErrorCode, e.CSErrorCode, e.ErrorText) } +type UUID string + +func (c UUID) MarshalJSON() ([]byte, error) { + return json.Marshal(string(c)) +} + +func (c *UUID) UnmarshalJSON(data []byte) error { + value := strings.Trim(string(data), "\"") + if strings.HasPrefix(string(data), "\"") { + *c = UUID(value) + return nil + } + _, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + *c = UUID(value) + return nil +} + type CloudStackClient struct { HTTPGETOnly bool // If `true` only use HTTP GET calls diff --git a/cloudstack/cloudstack_test.go b/cloudstack/cloudstack_test.go index 8f0b246..23f0f8c 100644 --- a/cloudstack/cloudstack_test.go +++ b/cloudstack/cloudstack_test.go @@ -87,3 +87,35 @@ func TestCreateSyncClient(t *testing.T) { t.Errorf("Failed to create Cloudstack Client") } } + +type UUIDStruct struct { + Value UUID `json:"value"` +} + +func TestUUID(t *testing.T) { + valLong := `{"value": 4801878}` + valString := `{"value": "994801878"}` + valBool := `{"value": false}` + res := UUIDStruct{} + + res.Value = "" + if err := json.Unmarshal([]byte(valLong), &res); err != nil { + t.Errorf("could not unserialize long into UUID: %s", err) + } + if res.Value != "4801878" { + t.Errorf("unepected value '%s', expecting 4801878", res.Value) + } + + res.Value = "" + if err := json.Unmarshal([]byte(valString), &res); err != nil { + t.Errorf("could not unserialize string into UUID: %s", err) + } + if res.Value != "994801878" { + t.Errorf("unepected value '%s', expecting 994801878", res.Value) + } + + res.Value = "" + if err := json.Unmarshal([]byte(valBool), &res); err == nil { + t.Errorf("missing expected error when serializing bool into UUID") + } +} diff --git a/generate/generate.go b/generate/generate.go index b82a030..a349c82 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -71,6 +71,14 @@ var nestedResponse = map[string]string{ "getUploadParamsForVolume": "getuploadparams", } +// longToStringConvertedParams is a prefilled map with the list of +// response fields that migrated from long to string within +// the current major baseline. This fields will be parsed from +// json as string and then fallback on long. +var longToStringConvertedParams = map[string]bool{ + "managementserverid": true, +} + // We prefill this one value to make sure it is not // created twice, as this is also a top level type. var typeNames = map[string]bool{"Nic": true} @@ -301,6 +309,27 @@ func (as *allServices) GeneralCode() ([]byte, error) { pn(" return fmt.Errorf(\"CloudStack API error %%d (CSExceptionErrorCode: %%d): %%s\", e.ErrorCode, e.CSErrorCode, e.ErrorText)") pn("}") pn("") + + pn("type UUID string") + pn("") + pn("func (c UUID) MarshalJSON() ([]byte, error) {") + pn(" return json.Marshal(string(c))") + pn("}") + pn("") + pn("func (c *UUID) UnmarshalJSON(data []byte) error {") + pn(" value := strings.Trim(string(data), \"\\\"\")") + pn(" if strings.HasPrefix(string(data), \"\\\"\") {") + pn(" *c = UUID(value)") + pn(" return nil") + pn(" }") + pn(" _, err := strconv.ParseInt(value, 10, 64)") + pn(" if err != nil {") + pn(" return err") + pn(" }") + pn(" *c = UUID(value)") + pn(" return nil") + pn("}") + pn("type CloudStackClient struct {") pn(" HTTPGETOnly bool // If `true` only use HTTP GET calls") pn("") @@ -1802,7 +1831,13 @@ func sourceDir() (string, error) { } func mapType(aName string, pName string, pType string) string { + if _, ok := longToStringConvertedParams[pName]; ok { + pType = "UUID" + } + switch pType { + case "UUID": + return "UUID" case "boolean": return "bool" case "short", "int", "integer":