From 4d02e1da8ec8ee486b3acff7693445e68cf5613b Mon Sep 17 00:00:00 2001 From: Gabriel Monroy Date: Tue, 1 Apr 2014 16:02:12 -0600 Subject: [PATCH] feat(etc-hosts) add support for manage_etc_hosts: localhost This feature is based on https://github.com/number5/cloud-init/blob/master/doc/examples/cloud-config.txt#L447:L482 --- Documentation/cloud-config.md | 13 +++++ initialize/config.go | 19 ++++++-- initialize/manage_etc_hosts.go | 44 +++++++++++++++++ initialize/manage_etc_hosts_test.go | 76 +++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 initialize/manage_etc_hosts.go create mode 100644 initialize/manage_etc_hosts_test.go diff --git a/Documentation/cloud-config.md b/Documentation/cloud-config.md index 5e0e2e0..21b9875 100644 --- a/Documentation/cloud-config.md +++ b/Documentation/cloud-config.md @@ -260,3 +260,16 @@ Provide a list of objects with the following attributes: - **content**: Data to write at the provided `path` - **permissions**: String representing file permissions in octal notation (i.e. '0644') - **owner**: User and group that should own the file written to disk. This is equivalent to the `:` argument to `chown : `. + +### manage_etc_hosts + +Have coreos-cloudinit manage your /etc/hosts file for local name resolution. +The only supported value is "localhost" which will cause your system's hostname +to resolve to "127.0.0.1". This is helpful when the host does not have DNS +infrastructure in place to resolve its own hostname, for example, when using Vagrant. + +``` +#cloud-config + +manage_etc_hosts: localhost +``` diff --git a/initialize/config.go b/initialize/config.go index 21e19e3..6213207 100644 --- a/initialize/config.go +++ b/initialize/config.go @@ -17,9 +17,10 @@ type CloudConfig struct { Units []system.Unit OEM OEMRelease } - WriteFiles []system.File `yaml:"write_files"` - Hostname string - Users []system.User + WriteFiles []system.File `yaml:"write_files"` + Hostname string + Users []system.User + ManageEtcHosts string `yaml:"manage_etc_hosts"` } func NewCloudConfig(contents string) (*CloudConfig, error) { @@ -154,7 +155,7 @@ func Apply(cfg CloudConfig, env *Environment) error { commands["systemd-networkd.service"] = "restart" } else { if unit.Command != "" { - commands[unit.Name] = unit.Command + commands[unit.Name] = unit.Command } } } @@ -169,5 +170,15 @@ func Apply(cfg CloudConfig, env *Environment) error { } } + if cfg.ManageEtcHosts != "" { + + if err := WriteEtcHosts(cfg.ManageEtcHosts, env.Root()); err != nil { + log.Fatalf("Failed to write /etc/hosts to filesystem: %v", err) + } + + log.Printf("Wrote /etc/hosts file to filesystem") + + } + return nil } diff --git a/initialize/manage_etc_hosts.go b/initialize/manage_etc_hosts.go new file mode 100644 index 0000000..6632e5e --- /dev/null +++ b/initialize/manage_etc_hosts.go @@ -0,0 +1,44 @@ +package initialize + +import ( + "errors" + "fmt" + "os" + "path" + + "github.com/coreos/coreos-cloudinit/system" +) + +const DefaultIpv4Address = "127.0.0.1" + +func generateEtcHosts(option string) (out string, err error) { + if option != "localhost" { + return "", errors.New("Invalid option to manage_etc_hosts") + } + + // use the operating system hostname + hostname, err := os.Hostname() + if err != nil { + return "", err + } + + return fmt.Sprintf("%s %s", DefaultIpv4Address, hostname), nil + +} + +// Write an /etc/hosts file +func WriteEtcHosts(option string, root string) error { + + etcHosts, err := generateEtcHosts(option) + if err != nil { + return err + } + + file := system.File{ + Path: path.Join(root, "etc", "hosts"), + RawFilePermissions: "0644", + Content: etcHosts, + } + + return system.WriteFile(&file) +} diff --git a/initialize/manage_etc_hosts_test.go b/initialize/manage_etc_hosts_test.go new file mode 100644 index 0000000..77f06bc --- /dev/null +++ b/initialize/manage_etc_hosts_test.go @@ -0,0 +1,76 @@ +package initialize + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "testing" +) + +func TestCloudConfigManageEtcHosts(t *testing.T) { + contents := ` +manage_etc_hosts: localhost +` + cfg, err := NewCloudConfig(contents) + if err != nil { + t.Fatalf("Encountered unexpected error: %v", err) + } + + manageEtcHosts := cfg.ManageEtcHosts + + if manageEtcHosts != "localhost" { + t.Errorf("ManageEtcHosts value is %q, expected 'localhost'", manageEtcHosts) + } +} + +func TestManageEtcHostsInvalidValue(t *testing.T) { + dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-") + if err != nil { + t.Fatalf("Unable to create tempdir: %v", err) + } + defer rmdir(dir) + + if err := WriteEtcHosts("invalid", dir); err == nil { + t.Fatalf("WriteEtcHosts succeeded with invalid value: %v", err) + } +} + +func TestEtcHostsWrittenToDisk(t *testing.T) { + dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-") + if err != nil { + t.Fatalf("Unable to create tempdir: %v", err) + } + defer rmdir(dir) + + if err := WriteEtcHosts("localhost", dir); err != nil { + t.Fatalf("WriteEtcHosts failed: %v", err) + } + + fullPath := path.Join(dir, "etc", "hosts") + + fi, err := os.Stat(fullPath) + if err != nil { + t.Fatalf("Unable to stat file: %v", err) + } + + if fi.Mode() != os.FileMode(0644) { + t.Errorf("File has incorrect mode: %v", fi.Mode()) + } + + contents, err := ioutil.ReadFile(fullPath) + if err != nil { + t.Fatalf("Unable to read expected file: %v", err) + } + + hostname, err := os.Hostname() + if err != nil { + t.Fatalf("Unable to read OS hostname: %v", err) + } + + expect := fmt.Sprintf("%s %s", DefaultIpv4Address, hostname) + + if string(contents) != expect { + t.Fatalf("File has incorrect contents") + } +}