diff --git a/initialize/meta_data.go b/initialize/meta_data.go index 05c0153..8fb1e77 100644 --- a/initialize/meta_data.go +++ b/initialize/meta_data.go @@ -4,7 +4,10 @@ import "encoding/json" // ParseMetaData parses a JSON blob in the OpenStack metadata service format, and // converts it to a partially hydrated CloudConfig -func ParseMetaData(contents string) (cfg *CloudConfig, err error) { +func ParseMetaData(contents string) (*CloudConfig, error) { + if len(contents) == 0 { + return nil, nil + } var metadata struct { SSHAuthorizedKeyMap map[string]string `json:"public_keys"` Hostname string `json:"hostname"` @@ -12,17 +15,20 @@ func ParseMetaData(contents string) (cfg *CloudConfig, err error) { ContentPath string `json:"content_path"` } `json:"network_config"` } - if err = json.Unmarshal([]byte(contents), &metadata); err != nil { - return + if err := json.Unmarshal([]byte(contents), &metadata); err != nil { + return nil, err } - cfg.SSHAuthorizedKeys = make([]string, 0, len(metadata.SSHAuthorizedKeyMap)) - for _, key := range metadata.SSHAuthorizedKeyMap { - cfg.SSHAuthorizedKeys = append(cfg.SSHAuthorizedKeys, key) + var cfg CloudConfig + if len(metadata.SSHAuthorizedKeyMap) > 0 { + cfg.SSHAuthorizedKeys = make([]string, 0, len(metadata.SSHAuthorizedKeyMap)) + for _, key := range metadata.SSHAuthorizedKeyMap { + cfg.SSHAuthorizedKeys = append(cfg.SSHAuthorizedKeys, key) + } } cfg.Hostname = metadata.Hostname cfg.NetworkConfigPath = metadata.NetworkConfig.ContentPath - return + return &cfg, nil } // ExtractIPsFromMetaData parses a JSON blob in the OpenStack metadata service format, diff --git a/initialize/meta_data_test.go b/initialize/meta_data_test.go index a362243..2553e31 100644 --- a/initialize/meta_data_test.go +++ b/initialize/meta_data_test.go @@ -3,6 +3,39 @@ package initialize import "reflect" import "testing" +func TestParseMetadata(t *testing.T) { + for i, tt := range []struct { + in string + want *CloudConfig + err bool + }{ + {"", nil, false}, + {`garbage, invalid json`, nil, true}, + {`{"foo": "bar"}`, &CloudConfig{}, false}, + {`{"network_config": {"content_path": "asdf"}}`, &CloudConfig{NetworkConfigPath: "asdf"}, false}, + {`{"hostname": "turkleton"}`, &CloudConfig{Hostname: "turkleton"}, false}, + {`{"public_keys": {"jack": "jill", "bob": "alice"}}`, &CloudConfig{SSHAuthorizedKeys: []string{"jill", "alice"}}, false}, + {`{"unknown": "thing", "hostname": "my_host", "public_keys": {"do": "re", "mi": "fa"}, "network_config": {"content_path": "/root", "blah": "zzz"}}`, &CloudConfig{SSHAuthorizedKeys: []string{"re", "fa"}, Hostname: "my_host", NetworkConfigPath: "/root"}, false}, + } { + got, err := ParseMetaData(tt.in) + if tt.err != (err != nil) { + t.Errorf("case #%d: bad error state: got %t, want %t (err=%v)", i, (err != nil), tt.err, err) + } + if got == nil { + if tt.want != nil { + t.Errorf("case #%d: unexpected nil output", i) + } + } else if tt.want == nil { + t.Errorf("case #%d: unexpected non-nil output", i) + } else { + if !reflect.DeepEqual(*got, *tt.want) { + t.Errorf("case #%d: bad output:\ngot\n%v\nwant\n%v", i, *got, *tt.want) + } + } + } + +} + func TestExtractIPsFromMetadata(t *testing.T) { for i, tt := range []struct { in []byte