From ba1c1e97d0a33f4948407d01d8c77661af186ffa Mon Sep 17 00:00:00 2001 From: Jonathan Boulle Date: Tue, 24 Jun 2014 17:46:06 -0700 Subject: [PATCH 1/3] cloudinit: retrieve IPv4 addresses from metadata This uses the new MetadataService implementation to retrieve values for $private_ipv4 and $public_ipv4 substitutions, instead of using environment variables. --- coreos-cloudinit.go | 11 ++++++++++- initialize/env.go | 8 ++------ initialize/env_test.go | 13 ++++++------- initialize/meta_data.go | 26 +++++++++++++++++++++++--- initialize/meta_data_test.go | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 17 deletions(-) create mode 100644 initialize/meta_data_test.go diff --git a/coreos-cloudinit.go b/coreos-cloudinit.go index 391ed2f..6f6db8e 100644 --- a/coreos-cloudinit.go +++ b/coreos-cloudinit.go @@ -86,7 +86,16 @@ func main() { die() } - env := initialize.NewEnvironment("/", ds.ConfigRoot(), workspace, convertNetconf, sshKeyName) + var subs map[string]string + if len(metadataBytes) > 0 { + subs, err = initialize.ExtractIPsFromMetadata(metadataBytes) + if err != nil { + fmt.Printf("Failed extracting IPs from meta-data: %v\n", err) + die() + } + } + env := initialize.NewEnvironment("/", ds.ConfigRoot(), workspace, convertNetconf, sshKeyName, subs) + if len(userdataBytes) > 0 { if err := processUserdata(string(userdataBytes), env); err != nil { fmt.Printf("Failed to process user-data: %v\n", err) diff --git a/initialize/env.go b/initialize/env.go index 948e51d..b2816e2 100644 --- a/initialize/env.go +++ b/initialize/env.go @@ -1,7 +1,6 @@ package initialize import ( - "os" "path" "strings" ) @@ -17,11 +16,8 @@ type Environment struct { substitutions map[string]string } -func NewEnvironment(root, configRoot, workspace, netconfType, sshKeyName string) *Environment { - substitutions := map[string]string{ - "$public_ipv4": os.Getenv("COREOS_PUBLIC_IPV4"), - "$private_ipv4": os.Getenv("COREOS_PRIVATE_IPV4"), - } +// TODO(jonboulle): this is getting unwieldy, should be able to simplify the interface somehow +func NewEnvironment(root, configRoot, workspace, netconfType, sshKeyName string, substitutions map[string]string) *Environment { return &Environment{root, configRoot, workspace, netconfType, sshKeyName, substitutions} } diff --git a/initialize/env_test.go b/initialize/env_test.go index 33ff933..3ff28de 100644 --- a/initialize/env_test.go +++ b/initialize/env_test.go @@ -1,14 +1,13 @@ package initialize -import ( - "os" - "testing" -) +import "testing" func TestEnvironmentApply(t *testing.T) { - os.Setenv("COREOS_PUBLIC_IPV4", "192.0.2.3") - os.Setenv("COREOS_PRIVATE_IPV4", "192.0.2.203") - env := NewEnvironment("./", "./", "./", "", "") + subs := map[string]string{ + "$public_ipv4": "192.0.2.3", + "$private_ipv4": "192.0.2.203", + } + env := NewEnvironment("./", "./", "./", "", "", subs) input := `[Service] ExecStart=/usr/bin/echo "$public_ipv4" ExecStop=/usr/bin/echo $private_ipv4 diff --git a/initialize/meta_data.go b/initialize/meta_data.go index d83b8b5..5168d36 100644 --- a/initialize/meta_data.go +++ b/initialize/meta_data.go @@ -1,9 +1,9 @@ package initialize -import ( - "encoding/json" -) +import "encoding/json" +// ParseMetaData parses a JSON blob in the OpenStack metadata service format, and +// converts it to a CloudConfig func ParseMetaData(contents string) (cfg CloudConfig, err error) { var metadata struct { SSHAuthorizedKeyMap map[string]string `json:"public_keys"` @@ -24,3 +24,23 @@ func ParseMetaData(contents string) (cfg CloudConfig, err error) { cfg.NetworkConfigPath = metadata.NetworkConfig.ContentPath return } + +// ExtractIPsFromMetaData parses a JSON blob in the OpenStack metadata service format, +// and returns a substitution map possibly containing private_ipv4 and public_ipv4 addresses +func ExtractIPsFromMetadata(contents []byte) (map[string]string, error) { + var ips struct { + Public string `json:"public-ipv4"` + Private string `json:"local-ipv4"` + } + if err := json.Unmarshal(contents, &ips); err != nil { + return nil, err + } + m := make(map[string]string) + if ips.Private != "" { + m["$private_ipv4"] = ips.Private + } + if ips.Public != "" { + m["$public_ipv4"] = ips.Public + } + return m, nil +} diff --git a/initialize/meta_data_test.go b/initialize/meta_data_test.go new file mode 100644 index 0000000..a362243 --- /dev/null +++ b/initialize/meta_data_test.go @@ -0,0 +1,36 @@ +package initialize + +import "reflect" +import "testing" + +func TestExtractIPsFromMetadata(t *testing.T) { + for i, tt := range []struct { + in []byte + err bool + out map[string]string + }{ + { + []byte(`{"public-ipv4": "12.34.56.78", "local-ipv4": "1.2.3.4"}`), + false, + map[string]string{"$public_ipv4": "12.34.56.78", "$private_ipv4": "1.2.3.4"}, + }, + { + []byte(`{"local-ipv4": "127.0.0.1", "something_else": "don't care"}`), + false, + map[string]string{"$private_ipv4": "127.0.0.1"}, + }, + { + []byte(`garbage`), + true, + nil, + }, + } { + got, err := ExtractIPsFromMetadata(tt.in) + if (err != nil) != tt.err { + t.Errorf("bad error state (got %t, want %t)", err != nil, tt.err) + } + if !reflect.DeepEqual(got, tt.out) { + t.Errorf("case %d: got %s, want %s", i, got, tt.out) + } + } +} From 439b7e8b9880b116bd1ecd7df803ef1bb2c4afd1 Mon Sep 17 00:00:00 2001 From: Jonathan Boulle Date: Tue, 24 Jun 2014 18:49:49 -0700 Subject: [PATCH 2/3] initialize/env: fall back to COREOS_*_IPV4 env variables --- initialize/env.go | 10 ++++++++ initialize/env_test.go | 52 ++++++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/initialize/env.go b/initialize/env.go index b2816e2..715c07d 100644 --- a/initialize/env.go +++ b/initialize/env.go @@ -1,6 +1,7 @@ package initialize import ( + "os" "path" "strings" ) @@ -18,6 +19,15 @@ type Environment struct { // TODO(jonboulle): this is getting unwieldy, should be able to simplify the interface somehow func NewEnvironment(root, configRoot, workspace, netconfType, sshKeyName string, substitutions map[string]string) *Environment { + // If certain values are not in the supplied substitution, fall back to retrieving them from the environment + for k, v := range map[string]string{ + "$public_ipv4": os.Getenv("COREOS_PUBLIC_IPV4"), + "$private_ipv4": os.Getenv("COREOS_PRIVATE_IPV4"), + } { + if _, ok := substitutions[k]; !ok { + substitutions[k] = v + } + } return &Environment{root, configRoot, workspace, netconfType, sshKeyName, substitutions} } diff --git a/initialize/env_test.go b/initialize/env_test.go index 3ff28de..8b572ac 100644 --- a/initialize/env_test.go +++ b/initialize/env_test.go @@ -1,26 +1,48 @@ package initialize -import "testing" +import ( + "os" + "testing" +) func TestEnvironmentApply(t *testing.T) { - subs := map[string]string{ - "$public_ipv4": "192.0.2.3", - "$private_ipv4": "192.0.2.203", - } - env := NewEnvironment("./", "./", "./", "", "", subs) - input := `[Service] + os.Setenv("COREOS_PUBLIC_IPV4", "1.2.3.4") + os.Setenv("COREOS_PRIVATE_IPV4", "5.6.7.8") + for _, tt := range []struct { + subs map[string]string + input string + out string + }{ + { + map[string]string{ + "$public_ipv4": "192.0.2.3", + "$private_ipv4": "192.0.2.203", + }, + `[Service] ExecStart=/usr/bin/echo "$public_ipv4" ExecStop=/usr/bin/echo $private_ipv4 -ExecStop=/usr/bin/echo $unknown -` - expected := `[Service] +ExecStop=/usr/bin/echo $unknown`, + `[Service] ExecStart=/usr/bin/echo "192.0.2.3" ExecStop=/usr/bin/echo 192.0.2.203 -ExecStop=/usr/bin/echo $unknown -` +ExecStop=/usr/bin/echo $unknown`, + }, + { + map[string]string{"$private_ipv4": "127.0.0.1"}, + "$private_ipv4\n$public_ipv4", + "127.0.0.1\n1.2.3.4", + }, + { + map[string]string{"foo": "bar"}, + "$private_ipv4\n$public_ipv4", + "5.6.7.8\n1.2.3.4", + }, + } { - output := env.Apply(input) - if output != expected { - t.Fatalf("Environment incorrectly applied.\nOutput:\n%s\nExpected:\n%s", output, expected) + env := NewEnvironment("./", "./", "./", "", "", tt.subs) + got := env.Apply(tt.input) + if got != tt.out { + t.Fatalf("Environment incorrectly applied.\ngot:\n%s\nwant:\n%s", got, tt.out) + } } } From 2805d70ece9a53031c7709be7eb4eff19d5035e3 Mon Sep 17 00:00:00 2001 From: Jonathan Boulle Date: Tue, 24 Jun 2014 18:52:08 -0700 Subject: [PATCH 3/3] initialize/env: add notes about tests --- initialize/env_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/initialize/env_test.go b/initialize/env_test.go index 8b572ac..756c957 100644 --- a/initialize/env_test.go +++ b/initialize/env_test.go @@ -14,6 +14,8 @@ func TestEnvironmentApply(t *testing.T) { out string }{ { + // Substituting both values directly should always take precedence + // over environment variables map[string]string{ "$public_ipv4": "192.0.2.3", "$private_ipv4": "192.0.2.203", @@ -28,11 +30,13 @@ ExecStop=/usr/bin/echo 192.0.2.203 ExecStop=/usr/bin/echo $unknown`, }, { + // Substituting one value directly while falling back with the other map[string]string{"$private_ipv4": "127.0.0.1"}, "$private_ipv4\n$public_ipv4", "127.0.0.1\n1.2.3.4", }, { + // Falling back to environment variables for both values map[string]string{"foo": "bar"}, "$private_ipv4\n$public_ipv4", "5.6.7.8\n1.2.3.4",