config: deprecate - in favor of _ for key names

In all of the YAML tags, - has been replaced with _. normalizeConfig() and
normalizeKeys() have also been added to perform the normalization of the input
cloud-config.

As part of the normalization process, falsey values are converted to "false".
The "off" update strategy is no exception and as a result the "off" update
strategy has been changed to "false".
This commit is contained in:
Alex Crawford 2014-11-03 12:09:52 -08:00
parent e9bda98b54
commit d02aa18839
9 changed files with 119 additions and 61 deletions

View File

@ -50,10 +50,13 @@ type CloudConfig struct {
// fields but log encountering them. // fields but log encountering them.
func NewCloudConfig(contents string) (*CloudConfig, error) { func NewCloudConfig(contents string) (*CloudConfig, error) {
var cfg CloudConfig var cfg CloudConfig
err := yaml.Unmarshal([]byte(contents), &cfg) ncontents, err := normalizeConfig(contents)
if err != nil { if err != nil {
return &cfg, err return &cfg, err
} }
if err = yaml.Unmarshal(ncontents, &cfg); err != nil {
return &cfg, err
}
warnOnUnrecognizedKeys(contents, log.Printf) warnOnUnrecognizedKeys(contents, log.Printf)
return &cfg, nil 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
}

View File

@ -200,7 +200,7 @@ coreos:
etcd: etcd:
discovery: "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877" discovery: "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877"
update: update:
reboot-strategy: reboot reboot_strategy: reboot
units: units:
- name: 50-eth0.network - name: 50-eth0.network
runtime: yes runtime: yes
@ -217,9 +217,9 @@ coreos:
oem: oem:
id: rackspace id: rackspace
name: Rackspace Cloud Servers name: Rackspace Cloud Servers
version-id: 168.0.0 version_id: 168.0.0
home-url: https://www.rackspace.com/cloud/servers/ home_url: https://www.rackspace.com/cloud/servers/
bug-report-url: https://github.com/coreos/coreos-overlay bug_report_url: https://github.com/coreos/coreos-overlay
ssh_authorized_keys: ssh_authorized_keys:
- foobar - foobar
- foobaz - foobaz
@ -354,18 +354,18 @@ func TestCloudConfigUsers(t *testing.T) {
users: users:
- name: elroy - name: elroy
passwd: somehash passwd: somehash
ssh-authorized-keys: ssh_authorized_keys:
- somekey - somekey
gecos: arbitrary comment gecos: arbitrary comment
homedir: /home/place homedir: /home/place
no-create-home: yes no_create_home: yes
primary-group: things primary_group: things
groups: groups:
- ping - ping
- pong - pong
no-user-group: true no_user_group: true
system: y system: y
no-log-init: True no_log_init: True
` `
cfg, err := NewCloudConfig(contents) cfg, err := NewCloudConfig(contents)
if err != nil { if err != nil {
@ -404,11 +404,11 @@ users:
} }
if !user.NoCreateHome { 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" { 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 { if len(user.Groups) != 2 {
@ -423,7 +423,7 @@ users:
} }
if !user.NoUserGroup { if !user.NoUserGroup {
t.Errorf("Failed to parse no-user-group field") t.Errorf("Failed to parse no_user_group field")
} }
if !user.System { if !user.System {
@ -431,7 +431,7 @@ users:
} }
if !user.NoLogInit { 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 := ` contents := `
users: users:
- name: elroy - name: elroy
coreos-ssh-import-github: bcwaldon coreos_ssh_import_github: bcwaldon
` `
cfg, err := NewCloudConfig(contents) cfg, err := NewCloudConfig(contents)
if err != nil { if err != nil {
@ -466,7 +466,7 @@ func TestCloudConfigUsersSSHImportURL(t *testing.T) {
contents := ` contents := `
users: users:
- name: elroy - 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) cfg, err := NewCloudConfig(contents)
if err != nil { 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) 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)
}
}
}

View File

@ -18,31 +18,31 @@ package config
type Etcd struct { type Etcd struct {
Addr string `yaml:"addr" env:"ETCD_ADDR"` Addr string `yaml:"addr" env:"ETCD_ADDR"`
BindAddr string `yaml:"bind-addr" env:"ETCD_BIND_ADDR"` BindAddr string `yaml:"bind_addr" env:"ETCD_BIND_ADDR"`
CAFile string `yaml:"ca-file" env:"ETCD_CA_FILE"` CAFile string `yaml:"ca_file" env:"ETCD_CA_FILE"`
CertFile string `yaml:"cert-file" env:"ETCD_CERT_FILE"` CertFile string `yaml:"cert_file" env:"ETCD_CERT_FILE"`
ClusterActiveSize string `yaml:"cluster-active-size" env:"ETCD_CLUSTER_ACTIVE_SIZE"` ClusterActiveSize string `yaml:"cluster_active_size" env:"ETCD_CLUSTER_ACTIVE_SIZE"`
ClusterRemoveDelay string `yaml:"cluster-remove-delay" env:"ETCD_CLUSTER_REMOVE_DELAY"` ClusterRemoveDelay string `yaml:"cluster_remove_delay" env:"ETCD_CLUSTER_REMOVE_DELAY"`
ClusterSyncInterval string `yaml:"cluster-sync-interval" env:"ETCD_CLUSTER_SYNC_INTERVAL"` ClusterSyncInterval string `yaml:"cluster_sync_interval" env:"ETCD_CLUSTER_SYNC_INTERVAL"`
Cors string `yaml:"cors" env:"ETCD_CORS"` Cors string `yaml:"cors" env:"ETCD_CORS"`
CPUProfileFile string `yaml:"cpu-profile-file" env:"ETCD_CPU_PROFILE_FILE"` CPUProfileFile string `yaml:"cpu_profile_file" env:"ETCD_CPU_PROFILE_FILE"`
DataDir string `yaml:"data-dir" env:"ETCD_DATA_DIR"` DataDir string `yaml:"data_dir" env:"ETCD_DATA_DIR"`
Discovery string `yaml:"discovery" env:"ETCD_DISCOVERY"` Discovery string `yaml:"discovery" env:"ETCD_DISCOVERY"`
HTTPReadTimeout string `yaml:"http-read-timeout" env:"ETCD_HTTP_READ_TIMEOUT"` HTTPReadTimeout string `yaml:"http_read_timeout" env:"ETCD_HTTP_READ_TIMEOUT"`
HTTPWriteTimeout string `yaml:"http-write-timeout" env:"ETCD_HTTP_WRITE_TIMEOUT"` HTTPWriteTimeout string `yaml:"http_write_timeout" env:"ETCD_HTTP_WRITE_TIMEOUT"`
KeyFile string `yaml:"key-file" env:"ETCD_KEY_FILE"` KeyFile string `yaml:"key_file" env:"ETCD_KEY_FILE"`
MaxClusterSize string `yaml:"max-cluster-size" env:"ETCD_MAX_CLUSTER_SIZE"` MaxClusterSize string `yaml:"max_cluster_size" env:"ETCD_MAX_CLUSTER_SIZE"`
MaxResultBuffer string `yaml:"max-result-buffer" env:"ETCD_MAX_RESULT_BUFFER"` MaxResultBuffer string `yaml:"max_result_buffer" env:"ETCD_MAX_RESULT_BUFFER"`
MaxRetryAttempts string `yaml:"max-retry-attempts" env:"ETCD_MAX_RETRY_ATTEMPTS"` MaxRetryAttempts string `yaml:"max_retry_attempts" env:"ETCD_MAX_RETRY_ATTEMPTS"`
Name string `yaml:"name" env:"ETCD_NAME"` Name string `yaml:"name" env:"ETCD_NAME"`
PeerAddr string `yaml:"peer-addr" env:"ETCD_PEER_ADDR"` PeerAddr string `yaml:"peer_addr" env:"ETCD_PEER_ADDR"`
PeerBindAddr string `yaml:"peer-bind-addr" env:"ETCD_PEER_BIND_ADDR"` PeerBindAddr string `yaml:"peer_bind_addr" env:"ETCD_PEER_BIND_ADDR"`
PeerCAFile string `yaml:"peer-ca-file" env:"ETCD_PEER_CA_FILE"` PeerCAFile string `yaml:"peer_ca_file" env:"ETCD_PEER_CA_FILE"`
PeerCertFile string `yaml:"peer-cert-file" env:"ETCD_PEER_CERT_FILE"` PeerCertFile string `yaml:"peer_cert_file" env:"ETCD_PEER_CERT_FILE"`
PeerKeyFile string `yaml:"peer-key-file" env:"ETCD_PEER_KEY_FILE"` PeerKeyFile string `yaml:"peer_key_file" env:"ETCD_PEER_KEY_FILE"`
Peers string `yaml:"peers" env:"ETCD_PEERS"` 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"` Snapshot string `yaml:"snapshot" env:"ETCD_SNAPSHOT"`
Verbose string `yaml:"verbose" env:"ETCD_VERBOSE"` 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"`
} }

View File

@ -17,14 +17,14 @@
package config package config
type Fleet struct { type Fleet struct {
AgentTTL string `yaml:"agent-ttl" env:"FLEET_AGENT_TTL"` AgentTTL string `yaml:"agent_ttl" env:"FLEET_AGENT_TTL"`
EngineReconcileInterval string `yaml:"engine-reconcile-interval" env:"FLEET_ENGINE_RECONCILE_INTERVAL"` EngineReconcileInterval string `yaml:"engine_reconcile_interval" env:"FLEET_ENGINE_RECONCILE_INTERVAL"`
EtcdCAFile string `yaml:"etcd-cafile" env:"FLEET_ETCD_CAFILE"` EtcdCAFile string `yaml:"etcd_cafile" env:"FLEET_ETCD_CAFILE"`
EtcdCertFile string `yaml:"etcd-certfile" env:"FLEET_ETCD_CERTFILE"` EtcdCertFile string `yaml:"etcd_certfile" env:"FLEET_ETCD_CERTFILE"`
EtcdKeyFile string `yaml:"etcd-keyfile" env:"FLEET_ETCD_KEYFILE"` EtcdKeyFile string `yaml:"etcd_keyfile" env:"FLEET_ETCD_KEYFILE"`
EtcdRequestTimeout string `yaml:"etcd-request-timeout" env:"FLEET_ETCD_REQUEST_TIMEOUT"` EtcdRequestTimeout string `yaml:"etcd_request_timeout" env:"FLEET_ETCD_REQUEST_TIMEOUT"`
EtcdServers string `yaml:"etcd-servers" env:"FLEET_ETCD_SERVERS"` EtcdServers string `yaml:"etcd_servers" env:"FLEET_ETCD_SERVERS"`
Metadata string `yaml:"metadata" env:"FLEET_METADATA"` 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"` Verbosity string `yaml:"verbosity" env:"FLEET_VERBOSITY"`
} }

View File

@ -19,7 +19,7 @@ package config
type OEM struct { type OEM struct {
ID string `yaml:"id"` ID string `yaml:"id"`
Name string `yaml:"name"` Name string `yaml:"name"`
VersionID string `yaml:"version-id"` VersionID string `yaml:"version_id"`
HomeURL string `yaml:"home-url"` HomeURL string `yaml:"home_url"`
BugReportURL string `yaml:"bug-report-url"` BugReportURL string `yaml:"bug_report_url"`
} }

View File

@ -17,7 +17,7 @@
package config package config
type Update struct { 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"` Group string `yaml:"group" env:"GROUP"`
Server string `yaml:"server" env:"SERVER"` Server string `yaml:"server" env:"SERVER"`
} }

View File

@ -19,15 +19,15 @@ package config
type User struct { type User struct {
Name string `yaml:"name"` Name string `yaml:"name"`
PasswordHash string `yaml:"passwd"` PasswordHash string `yaml:"passwd"`
SSHAuthorizedKeys []string `yaml:"ssh-authorized-keys"` SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"`
SSHImportGithubUser string `yaml:"coreos-ssh-import-github"` SSHImportGithubUser string `yaml:"coreos_ssh_import_github"`
SSHImportURL string `yaml:"coreos-ssh-import-url"` SSHImportURL string `yaml:"coreos_ssh_import_url"`
GECOS string `yaml:"gecos"` GECOS string `yaml:"gecos"`
Homedir string `yaml:"homedir"` Homedir string `yaml:"homedir"`
NoCreateHome bool `yaml:"no-create-home"` NoCreateHome bool `yaml:"no_create_home"`
PrimaryGroup string `yaml:"primary-group"` PrimaryGroup string `yaml:"primary_group"`
Groups []string `yaml:"groups"` Groups []string `yaml:"groups"`
NoUserGroup bool `yaml:"no-user-group"` NoUserGroup bool `yaml:"no_user_group"`
System bool `yaml:"system"` System bool `yaml:"system"`
NoLogInit bool `yaml:"no-log-init"` NoLogInit bool `yaml:"no_log_init"`
} }

View File

@ -126,7 +126,7 @@ func (uc Update) Units() []Unit {
Runtime: true, Runtime: true,
}} }}
if uc.Update.RebootStrategy == "off" { if uc.Update.RebootStrategy == "false" {
ls.Command = "stop" ls.Command = "stop"
ls.Mask = true ls.Mask = true
} }

View File

@ -73,7 +73,7 @@ func TestUpdateUnits(t *testing.T) {
}}}, }}},
}, },
{ {
config: config.Update{RebootStrategy: "off"}, config: config.Update{RebootStrategy: "false"},
units: []Unit{{config.Unit{ units: []Unit{{config.Unit{
Name: "locksmithd.service", Name: "locksmithd.service",
Command: "stop", Command: "stop",
@ -101,7 +101,7 @@ func TestUpdateFile(t *testing.T) {
}, },
{ {
config: config.Update{RebootStrategy: "wizzlewazzle"}, 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"}, 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{ file: &File{config.File{
Content: "REBOOT_STRATEGY=off\n", Content: "REBOOT_STRATEGY=false\n",
Path: "etc/coreos/update.conf", Path: "etc/coreos/update.conf",
RawFilePermissions: "0644", RawFilePermissions: "0644",
}}, }},