From 0923f1fca18db459f4fb2062508e72f3e5e70f39 Mon Sep 17 00:00:00 2001 From: Rafal Jeczalik Date: Sun, 1 Jan 2017 22:32:50 +0100 Subject: [PATCH] kd: workaround for YAML -> JSON template reencoding Works around the following yaml issue: https://github.com/go-yaml/yaml/issues/139 --- go/src/koding/kites/kloud/stack/kloud.go | 2 +- .../klientctl/endpoint/stack/export_test.go | 3 +- .../koding/klientctl/endpoint/stack/stack.go | 42 +++++++++++--- .../klientctl/endpoint/stack/stack_test.go | 58 +++++++++++++++++++ 4 files changed, 96 insertions(+), 9 deletions(-) diff --git a/go/src/koding/kites/kloud/stack/kloud.go b/go/src/koding/kites/kloud/stack/kloud.go index 67e8bb4103d..3806dbe5290 100644 --- a/go/src/koding/kites/kloud/stack/kloud.go +++ b/go/src/koding/kites/kloud/stack/kloud.go @@ -171,7 +171,7 @@ func ReadProviders(template []byte) ([]string, error) { return nil, err } - providers := make([]string, len(v.Provider)) + providers := make([]string, 0, len(v.Provider)) for p := range v.Provider { providers = append(providers, p) diff --git a/go/src/koding/klientctl/endpoint/stack/export_test.go b/go/src/koding/klientctl/endpoint/stack/export_test.go index b305db41e78..6797505338d 100644 --- a/go/src/koding/klientctl/endpoint/stack/export_test.go +++ b/go/src/koding/klientctl/endpoint/stack/export_test.go @@ -1,3 +1,4 @@ package stack -func FixHCL(v interface{}) { fixHCL(v) } +func FixHCL(v interface{}) { fixHCL(v) } +func FixYAML(v interface{}) interface{} { return fixYAML(v) } diff --git a/go/src/koding/klientctl/endpoint/stack/stack.go b/go/src/koding/klientctl/endpoint/stack/stack.go index b4c6db0c2dc..52ad4ff8399 100644 --- a/go/src/koding/klientctl/endpoint/stack/stack.go +++ b/go/src/koding/klientctl/endpoint/stack/stack.go @@ -15,7 +15,6 @@ import ( yaml "gopkg.in/yaml.v2" ) -// CreateOptions type CreateOptions struct { Team string Title string @@ -23,7 +22,6 @@ type CreateOptions struct { Template []byte } -// Valid func (opts *CreateOptions) Valid() error { if opts == nil { return errors.New("stack: arguments are missing") @@ -36,16 +34,13 @@ func (opts *CreateOptions) Valid() error { return nil } -// DefaultClient -var DefaultClient Client +var DefaultClient = &Client{} -// Client type Client struct { Kloud *kloud.Client Credential *credential.Client } -// Create func (c *Client) Create(opts *CreateOptions) (*stack.ImportResponse, error) { if err := opts.Valid(); err != nil { return nil, err @@ -135,7 +130,7 @@ func (c *Client) jsonReencode(data []byte) ([]byte, error) { var ymlv interface{} if err := yaml.Unmarshal(data, &ymlv); err == nil { - return json.Marshal(ymlv) + return json.Marshal(fixYAML(ymlv)) } var hclv interface{} @@ -154,6 +149,39 @@ func Create(opts *CreateOptions) (*stack.ImportResponse, error) { return DefaultClient.Create(opts) } +// fixYAML is a best-effort of fixing representation of +// YAML-encoded value, so it can be marshaled to a valid JSON. +// +// YAML creates types like map[interface{}]interface{}, which are +// not a valid JSON types. +// +// Related issue: +// +// https://github.com/go-yaml/yaml/issues/139 +// +func fixYAML(v interface{}) interface{} { + switch v := v.(type) { + case map[interface{}]interface{}: + fixedV := make(map[string]interface{}, len(v)) + + for k, v := range v { + fixedV[fmt.Sprintf("%v", k)] = fixYAML(v) + } + + return fixedV + case []interface{}: + fixedV := make([]interface{}, len(v)) + + for i := range v { + fixedV[i] = fixYAML(v[i]) + } + + return fixedV + default: + return v + } +} + // fixHCL is a best-effort method to "fix" value representation of // HCL-encoded value, so it can be marshaled to a valid JSON. // diff --git a/go/src/koding/klientctl/endpoint/stack/stack_test.go b/go/src/koding/klientctl/endpoint/stack/stack_test.go index fc0622c6a8a..0589d1f459b 100644 --- a/go/src/koding/klientctl/endpoint/stack/stack_test.go +++ b/go/src/koding/klientctl/endpoint/stack/stack_test.go @@ -102,6 +102,64 @@ func TestFixHCL(t *testing.T) { } } +func TestFixYAML(t *testing.T) { + cases := map[string]struct { + yaml interface{} + want interface{} + }{ + "fix map": { + map[interface{}]interface{}{ + "abc": 1, + }, + map[string]interface{}{ + "abc": 1, + }, + }, + "fix slice of maps": { + []interface{}{ + "abc", + map[interface{}]interface{}{ + "abc": 1, + }, + 1, + }, + []interface{}{ + "abc", + map[string]interface{}{ + "abc": 1, + }, + 1, + }, + }, + "fix nested maps": { + map[interface{}]interface{}{ + "abc": map[interface{}]interface{}{ + "abc": map[interface{}]interface{}{ + "abc": 1, + }, + }, + }, + map[string]interface{}{ + "abc": map[string]interface{}{ + "abc": map[string]interface{}{ + "abc": 1, + }, + }, + }, + }, + } + + for name, cas := range cases { + t.Run(name, func(t *testing.T) { + got := stack.FixYAML(cas.yaml) + + if !reflect.DeepEqual(got, cas.want) { + t.Fatalf("got %#v, want %#v", got, cas.want) + } + }) + } +} + func mustJSON(v interface{}) []byte { p, err := json.MarshalIndent(v, "", "\t") if err != nil {