diff --git a/config/config.go b/config/config.go index e3378d2..6049d2f 100644 --- a/config/config.go +++ b/config/config.go @@ -50,10 +50,13 @@ type CloudConfig struct { // fields but log encountering them. func NewCloudConfig(contents string) (*CloudConfig, error) { var cfg CloudConfig - err := yaml.Unmarshal([]byte(contents), &cfg) + ncontents, err := normalizeConfig(contents) if err != nil { return &cfg, err } + if err = yaml.Unmarshal(ncontents, &cfg); err != nil { + return &cfg, err + } warnOnUnrecognizedKeys(contents, log.Printf) return &cfg, nil } @@ -212,3 +215,31 @@ func warnOnUnrecognizedKeys(contents string, warn warner) { } } } + +func normalizeConfig(config string) ([]byte, error) { + var cfg map[interface{}]interface{} + if err := yaml.Unmarshal([]byte(config), &cfg); err != nil { + return nil, err + } + return yaml.Marshal(normalizeKeys(cfg)) +} + +func normalizeKeys(m map[interface{}]interface{}) map[interface{}]interface{} { + for k, v := range m { + if m, ok := m[k].(map[interface{}]interface{}); ok { + normalizeKeys(m) + } + + if s, ok := m[k].([]interface{}); ok { + for _, e := range s { + if m, ok := e.(map[interface{}]interface{}); ok { + normalizeKeys(m) + } + } + } + + delete(m, k) + m[strings.Replace(fmt.Sprint(k), "-", "_", -1)] = v + } + return m +} diff --git a/config/config_test.go b/config/config_test.go index 389518a..1d776d6 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -200,7 +200,7 @@ coreos: etcd: discovery: "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877" update: - reboot-strategy: reboot + reboot_strategy: reboot units: - name: 50-eth0.network runtime: yes @@ -217,9 +217,9 @@ coreos: oem: id: rackspace name: Rackspace Cloud Servers - version-id: 168.0.0 - home-url: https://www.rackspace.com/cloud/servers/ - bug-report-url: https://github.com/coreos/coreos-overlay + version_id: 168.0.0 + home_url: https://www.rackspace.com/cloud/servers/ + bug_report_url: https://github.com/coreos/coreos-overlay ssh_authorized_keys: - foobar - foobaz @@ -354,18 +354,18 @@ func TestCloudConfigUsers(t *testing.T) { users: - name: elroy passwd: somehash - ssh-authorized-keys: + ssh_authorized_keys: - somekey gecos: arbitrary comment homedir: /home/place - no-create-home: yes - primary-group: things + no_create_home: yes + primary_group: things groups: - ping - pong - no-user-group: true + no_user_group: true system: y - no-log-init: True + no_log_init: True ` cfg, err := NewCloudConfig(contents) if err != nil { @@ -404,11 +404,11 @@ users: } if !user.NoCreateHome { - t.Errorf("Failed to parse no-create-home field") + t.Errorf("Failed to parse no_create_home field") } if user.PrimaryGroup != "things" { - t.Errorf("Failed to parse primary-group field, got %q", user.PrimaryGroup) + t.Errorf("Failed to parse primary_group field, got %q", user.PrimaryGroup) } if len(user.Groups) != 2 { @@ -423,7 +423,7 @@ users: } if !user.NoUserGroup { - t.Errorf("Failed to parse no-user-group field") + t.Errorf("Failed to parse no_user_group field") } if !user.System { @@ -431,7 +431,7 @@ users: } if !user.NoLogInit { - t.Errorf("Failed to parse no-log-init field") + t.Errorf("Failed to parse no_log_init field") } } @@ -440,7 +440,7 @@ func TestCloudConfigUsersGithubUser(t *testing.T) { contents := ` users: - name: elroy - coreos-ssh-import-github: bcwaldon + coreos_ssh_import_github: bcwaldon ` cfg, err := NewCloudConfig(contents) if err != nil { @@ -466,7 +466,7 @@ func TestCloudConfigUsersSSHImportURL(t *testing.T) { contents := ` users: - name: elroy - coreos-ssh-import-url: https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys + coreos_ssh_import_url: https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys ` cfg, err := NewCloudConfig(contents) if err != nil { @@ -487,3 +487,30 @@ users: t.Errorf("ssh import url is %q, expected 'https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys'", user.SSHImportURL) } } + +func TestNormalizeKeys(t *testing.T) { + for _, tt := range []struct { + in string + out string + }{ + {"my_key_name: the-value\n", "my_key_name: the-value\n"}, + {"my-key_name: the-value\n", "my_key_name: the-value\n"}, + {"my-key-name: the-value\n", "my_key_name: the-value\n"}, + + {"a:\n- key_name: the-value\n", "a:\n- key_name: the-value\n"}, + {"a:\n- key-name: the-value\n", "a:\n- key_name: the-value\n"}, + + {"a:\n b:\n - key_name: the-value\n", "a:\n b:\n - key_name: the-value\n"}, + {"a:\n b:\n - key-name: the-value\n", "a:\n b:\n - key_name: the-value\n"}, + + {"coreos:\n update:\n reboot-strategy: off\n", "coreos:\n update:\n reboot_strategy: false\n"}, + } { + out, err := normalizeConfig(tt.in) + if err != nil { + t.Fatalf("bad error (%q): want nil, got %s", tt.in, err) + } + if string(out) != tt.out { + t.Fatalf("bad normalization (%q): want %q, got %q", tt.in, tt.out, out) + } + } +} diff --git a/config/etcd.go b/config/etcd.go index c2a95db..2adc4a2 100644 --- a/config/etcd.go +++ b/config/etcd.go @@ -18,31 +18,31 @@ package config type Etcd struct { Addr string `yaml:"addr" env:"ETCD_ADDR"` - BindAddr string `yaml:"bind-addr" env:"ETCD_BIND_ADDR"` - CAFile string `yaml:"ca-file" env:"ETCD_CA_FILE"` - CertFile string `yaml:"cert-file" env:"ETCD_CERT_FILE"` - ClusterActiveSize string `yaml:"cluster-active-size" env:"ETCD_CLUSTER_ACTIVE_SIZE"` - ClusterRemoveDelay string `yaml:"cluster-remove-delay" env:"ETCD_CLUSTER_REMOVE_DELAY"` - ClusterSyncInterval string `yaml:"cluster-sync-interval" env:"ETCD_CLUSTER_SYNC_INTERVAL"` + BindAddr string `yaml:"bind_addr" env:"ETCD_BIND_ADDR"` + CAFile string `yaml:"ca_file" env:"ETCD_CA_FILE"` + CertFile string `yaml:"cert_file" env:"ETCD_CERT_FILE"` + ClusterActiveSize string `yaml:"cluster_active_size" env:"ETCD_CLUSTER_ACTIVE_SIZE"` + ClusterRemoveDelay string `yaml:"cluster_remove_delay" env:"ETCD_CLUSTER_REMOVE_DELAY"` + ClusterSyncInterval string `yaml:"cluster_sync_interval" env:"ETCD_CLUSTER_SYNC_INTERVAL"` Cors string `yaml:"cors" env:"ETCD_CORS"` - CPUProfileFile string `yaml:"cpu-profile-file" env:"ETCD_CPU_PROFILE_FILE"` - DataDir string `yaml:"data-dir" env:"ETCD_DATA_DIR"` + CPUProfileFile string `yaml:"cpu_profile_file" env:"ETCD_CPU_PROFILE_FILE"` + DataDir string `yaml:"data_dir" env:"ETCD_DATA_DIR"` Discovery string `yaml:"discovery" env:"ETCD_DISCOVERY"` - HTTPReadTimeout string `yaml:"http-read-timeout" env:"ETCD_HTTP_READ_TIMEOUT"` - HTTPWriteTimeout string `yaml:"http-write-timeout" env:"ETCD_HTTP_WRITE_TIMEOUT"` - KeyFile string `yaml:"key-file" env:"ETCD_KEY_FILE"` - MaxClusterSize string `yaml:"max-cluster-size" env:"ETCD_MAX_CLUSTER_SIZE"` - MaxResultBuffer string `yaml:"max-result-buffer" env:"ETCD_MAX_RESULT_BUFFER"` - MaxRetryAttempts string `yaml:"max-retry-attempts" env:"ETCD_MAX_RETRY_ATTEMPTS"` + HTTPReadTimeout string `yaml:"http_read_timeout" env:"ETCD_HTTP_READ_TIMEOUT"` + HTTPWriteTimeout string `yaml:"http_write_timeout" env:"ETCD_HTTP_WRITE_TIMEOUT"` + KeyFile string `yaml:"key_file" env:"ETCD_KEY_FILE"` + MaxClusterSize string `yaml:"max_cluster_size" env:"ETCD_MAX_CLUSTER_SIZE"` + MaxResultBuffer string `yaml:"max_result_buffer" env:"ETCD_MAX_RESULT_BUFFER"` + MaxRetryAttempts string `yaml:"max_retry_attempts" env:"ETCD_MAX_RETRY_ATTEMPTS"` Name string `yaml:"name" env:"ETCD_NAME"` - PeerAddr string `yaml:"peer-addr" env:"ETCD_PEER_ADDR"` - PeerBindAddr string `yaml:"peer-bind-addr" env:"ETCD_PEER_BIND_ADDR"` - PeerCAFile string `yaml:"peer-ca-file" env:"ETCD_PEER_CA_FILE"` - PeerCertFile string `yaml:"peer-cert-file" env:"ETCD_PEER_CERT_FILE"` - PeerKeyFile string `yaml:"peer-key-file" env:"ETCD_PEER_KEY_FILE"` + PeerAddr string `yaml:"peer_addr" env:"ETCD_PEER_ADDR"` + PeerBindAddr string `yaml:"peer_bind_addr" env:"ETCD_PEER_BIND_ADDR"` + PeerCAFile string `yaml:"peer_ca_file" env:"ETCD_PEER_CA_FILE"` + PeerCertFile string `yaml:"peer_cert_file" env:"ETCD_PEER_CERT_FILE"` + PeerKeyFile string `yaml:"peer_key_file" env:"ETCD_PEER_KEY_FILE"` Peers string `yaml:"peers" env:"ETCD_PEERS"` - PeersFile string `yaml:"peers-file" env:"ETCD_PEERS_FILE"` + PeersFile string `yaml:"peers_file" env:"ETCD_PEERS_FILE"` Snapshot string `yaml:"snapshot" env:"ETCD_SNAPSHOT"` Verbose string `yaml:"verbose" env:"ETCD_VERBOSE"` - VeryVerbose string `yaml:"very-verbose" env:"ETCD_VERY_VERBOSE"` + VeryVerbose string `yaml:"very_verbose" env:"ETCD_VERY_VERBOSE"` } diff --git a/config/fleet.go b/config/fleet.go index 63675d3..63fbb87 100644 --- a/config/fleet.go +++ b/config/fleet.go @@ -17,14 +17,14 @@ package config type Fleet struct { - AgentTTL string `yaml:"agent-ttl" env:"FLEET_AGENT_TTL"` - EngineReconcileInterval string `yaml:"engine-reconcile-interval" env:"FLEET_ENGINE_RECONCILE_INTERVAL"` - EtcdCAFile string `yaml:"etcd-cafile" env:"FLEET_ETCD_CAFILE"` - EtcdCertFile string `yaml:"etcd-certfile" env:"FLEET_ETCD_CERTFILE"` - EtcdKeyFile string `yaml:"etcd-keyfile" env:"FLEET_ETCD_KEYFILE"` - EtcdRequestTimeout string `yaml:"etcd-request-timeout" env:"FLEET_ETCD_REQUEST_TIMEOUT"` - EtcdServers string `yaml:"etcd-servers" env:"FLEET_ETCD_SERVERS"` + AgentTTL string `yaml:"agent_ttl" env:"FLEET_AGENT_TTL"` + EngineReconcileInterval string `yaml:"engine_reconcile_interval" env:"FLEET_ENGINE_RECONCILE_INTERVAL"` + EtcdCAFile string `yaml:"etcd_cafile" env:"FLEET_ETCD_CAFILE"` + EtcdCertFile string `yaml:"etcd_certfile" env:"FLEET_ETCD_CERTFILE"` + EtcdKeyFile string `yaml:"etcd_keyfile" env:"FLEET_ETCD_KEYFILE"` + EtcdRequestTimeout string `yaml:"etcd_request_timeout" env:"FLEET_ETCD_REQUEST_TIMEOUT"` + EtcdServers string `yaml:"etcd_servers" env:"FLEET_ETCD_SERVERS"` Metadata string `yaml:"metadata" env:"FLEET_METADATA"` - PublicIP string `yaml:"public-ip" env:"FLEET_PUBLIC_IP"` + PublicIP string `yaml:"public_ip" env:"FLEET_PUBLIC_IP"` Verbosity string `yaml:"verbosity" env:"FLEET_VERBOSITY"` } diff --git a/config/oem.go b/config/oem.go index ad596a5..a76ee9f 100644 --- a/config/oem.go +++ b/config/oem.go @@ -19,7 +19,7 @@ package config type OEM struct { ID string `yaml:"id"` Name string `yaml:"name"` - VersionID string `yaml:"version-id"` - HomeURL string `yaml:"home-url"` - BugReportURL string `yaml:"bug-report-url"` + VersionID string `yaml:"version_id"` + HomeURL string `yaml:"home_url"` + BugReportURL string `yaml:"bug_report_url"` } diff --git a/config/update.go b/config/update.go index deba488..adbda30 100644 --- a/config/update.go +++ b/config/update.go @@ -17,7 +17,7 @@ package config type Update struct { - RebootStrategy string `yaml:"reboot-strategy" env:"REBOOT_STRATEGY" valid:"best-effort,etcd-lock,reboot,off"` + RebootStrategy string `yaml:"reboot_strategy" env:"REBOOT_STRATEGY" valid:"best-effort,etcd-lock,reboot,false"` Group string `yaml:"group" env:"GROUP"` Server string `yaml:"server" env:"SERVER"` } diff --git a/config/user.go b/config/user.go index 1abf936..44e959c 100644 --- a/config/user.go +++ b/config/user.go @@ -19,15 +19,15 @@ package config type User struct { Name string `yaml:"name"` PasswordHash string `yaml:"passwd"` - SSHAuthorizedKeys []string `yaml:"ssh-authorized-keys"` - SSHImportGithubUser string `yaml:"coreos-ssh-import-github"` - SSHImportURL string `yaml:"coreos-ssh-import-url"` + SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"` + SSHImportGithubUser string `yaml:"coreos_ssh_import_github"` + SSHImportURL string `yaml:"coreos_ssh_import_url"` GECOS string `yaml:"gecos"` Homedir string `yaml:"homedir"` - NoCreateHome bool `yaml:"no-create-home"` - PrimaryGroup string `yaml:"primary-group"` + NoCreateHome bool `yaml:"no_create_home"` + PrimaryGroup string `yaml:"primary_group"` Groups []string `yaml:"groups"` - NoUserGroup bool `yaml:"no-user-group"` + NoUserGroup bool `yaml:"no_user_group"` System bool `yaml:"system"` - NoLogInit bool `yaml:"no-log-init"` + NoLogInit bool `yaml:"no_log_init"` } diff --git a/system/update.go b/system/update.go index ced1133..2038def 100644 --- a/system/update.go +++ b/system/update.go @@ -126,7 +126,7 @@ func (uc Update) Units() []Unit { Runtime: true, }} - if uc.Update.RebootStrategy == "off" { + if uc.Update.RebootStrategy == "false" { ls.Command = "stop" ls.Mask = true } diff --git a/system/update_test.go b/system/update_test.go index 02dead4..a75348e 100644 --- a/system/update_test.go +++ b/system/update_test.go @@ -73,7 +73,7 @@ func TestUpdateUnits(t *testing.T) { }}}, }, { - config: config.Update{RebootStrategy: "off"}, + config: config.Update{RebootStrategy: "false"}, units: []Unit{{config.Unit{ Name: "locksmithd.service", Command: "stop", @@ -101,7 +101,7 @@ func TestUpdateFile(t *testing.T) { }, { config: config.Update{RebootStrategy: "wizzlewazzle"}, - err: errors.New("invalid value \"wizzlewazzle\" for option \"RebootStrategy\" (valid options: \"best-effort,etcd-lock,reboot,off\")"), + err: errors.New("invalid value \"wizzlewazzle\" for option \"RebootStrategy\" (valid options: \"best-effort,etcd-lock,reboot,false\")"), }, { config: config.Update{Group: "master", Server: "http://foo.com"}, @@ -136,9 +136,9 @@ func TestUpdateFile(t *testing.T) { }}, }, { - config: config.Update{RebootStrategy: "off"}, + config: config.Update{RebootStrategy: "false"}, file: &File{config.File{ - Content: "REBOOT_STRATEGY=off\n", + Content: "REBOOT_STRATEGY=false\n", Path: "etc/coreos/update.conf", RawFilePermissions: "0644", }},