Merge pull request #25 from bcwaldon/etcd-env-file
Write environment file from coreos.etcd options
This commit is contained in:
commit
3df9c40520
@ -74,12 +74,38 @@ Provide a list of objects with the following attributes:
|
|||||||
|
|
||||||
## Custom cloud-config Parameters
|
## Custom cloud-config Parameters
|
||||||
|
|
||||||
### coreos.etcd.discovery_url
|
### coreos.etcd
|
||||||
|
|
||||||
The value of `coreos.etcd.discovery_url` will be used to discover the instance's etcd peers using the [etcd discovery protocol][disco-proto]. Usage of the [public discovery service][disco-service] is encouraged.
|
The `coreos.etcd.*` options are translated to a partial systemd unit acting as an etcd configuration file.
|
||||||
|
`coreos-cloudinit` will also replace the strings `$private_ipv4` and `$public_ipv4` with the values generated by CoreOS based on a given provider.
|
||||||
|
|
||||||
[disco-proto]: https://github.com/coreos/etcd/blob/master/Documentation/discovery-protocol.md
|
For example, the following cloud-config document...
|
||||||
[disco-service]: http://discovery.etcd.io
|
|
||||||
|
```
|
||||||
|
#cloud-config
|
||||||
|
|
||||||
|
coreos:
|
||||||
|
etcd:
|
||||||
|
name: node001
|
||||||
|
discovery-url: https://discovery.etcd.io/3445fa65423d8b04df07f59fb40218f8
|
||||||
|
bind-addr: $public_ipv4:4001
|
||||||
|
peer-bind-addr: $private_ipv4:7001
|
||||||
|
```
|
||||||
|
|
||||||
|
...will generate a systemd snippet like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Service]
|
||||||
|
Environment="ETCD_NAME=node001""
|
||||||
|
Environment="ETCD_DISCOVERY_URL=https://discovery.etcd.io/3445fa65423d8b04df07f59fb40218f8"
|
||||||
|
Environment="ETCD_BIND_ADDR=203.0.113.29:4001"
|
||||||
|
Environment="ETCD_PEER_BIND_ADDR=192.0.2.13:7001"
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information about the available configuration options, see the [etcd documentation][etcd-config].
|
||||||
|
Note that hyphens in the coreos.etcd.* keys are mapped to underscores.
|
||||||
|
|
||||||
|
[etcd-config]: https://github.com/coreos/etcd/blob/master/Documentation/configuration.md
|
||||||
|
|
||||||
### coreos.units
|
### coreos.units
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ const DefaultSSHKeyName = "coreos-cloudinit"
|
|||||||
type CloudConfig struct {
|
type CloudConfig struct {
|
||||||
SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"`
|
SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"`
|
||||||
Coreos struct {
|
Coreos struct {
|
||||||
Etcd struct{ Discovery_URL string }
|
Etcd EtcdEnvironment
|
||||||
Fleet struct{ Autostart bool }
|
Fleet struct{ Autostart bool }
|
||||||
Units []Unit
|
Units []Unit
|
||||||
}
|
}
|
||||||
@ -98,13 +98,12 @@ func ApplyCloudConfig(cfg CloudConfig, sshKeyName string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Coreos.Etcd.Discovery_URL != "" {
|
if len(cfg.Coreos.Etcd) > 0 {
|
||||||
err := PersistEtcdDiscoveryURL(cfg.Coreos.Etcd.Discovery_URL)
|
if err := WriteEtcdEnvironment("/", cfg.Coreos.Etcd); err != nil {
|
||||||
if err == nil {
|
log.Fatalf("Failed to write etcd config to filesystem: %v", err)
|
||||||
log.Printf("Consumed etcd discovery url")
|
|
||||||
} else {
|
|
||||||
log.Fatalf("Failed to persist etcd discovery url to filesystem: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Wrote etcd config file to filesystem")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cfg.Coreos.Units) > 0 {
|
if len(cfg.Coreos.Units) > 0 {
|
||||||
|
@ -17,10 +17,6 @@ func TestCloudConfigEmpty(t *testing.T) {
|
|||||||
t.Error("Parsed incorrect number of SSH keys")
|
t.Error("Parsed incorrect number of SSH keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Coreos.Etcd.Discovery_URL != "" {
|
|
||||||
t.Error("Parsed incorrect value of discovery url")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Coreos.Fleet.Autostart {
|
if cfg.Coreos.Fleet.Autostart {
|
||||||
t.Error("Expected AutostartFleet not to be defined")
|
t.Error("Expected AutostartFleet not to be defined")
|
||||||
}
|
}
|
||||||
@ -81,10 +77,6 @@ hostname: trontastic
|
|||||||
t.Error("Expected first SSH key to be 'foobaz'")
|
t.Error("Expected first SSH key to be 'foobaz'")
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Coreos.Etcd.Discovery_URL != "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877" {
|
|
||||||
t.Error("Failed to parse etcd discovery url")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cfg.Coreos.Fleet.Autostart {
|
if !cfg.Coreos.Fleet.Autostart {
|
||||||
t.Error("Expected AutostartFleet to be true")
|
t.Error("Expected AutostartFleet to be true")
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,48 @@
|
|||||||
package cloudinit
|
package cloudinit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
type EtcdEnvironment map[string]string
|
||||||
etcdDiscoveryPath = "/var/run/etcd/bootstrap.disco"
|
|
||||||
)
|
|
||||||
|
|
||||||
func PersistEtcdDiscoveryURL(url string) error {
|
func (ec EtcdEnvironment) String() (out string) {
|
||||||
dir := path.Dir(etcdDiscoveryPath)
|
public := os.Getenv("COREOS_PUBLIC_IPV4")
|
||||||
if _, err := os.Stat(dir); err != nil {
|
private := os.Getenv("COREOS_PRIVATE_IPV4")
|
||||||
log.Printf("Creating directory /var/run/etcd")
|
|
||||||
err := os.MkdirAll(dir, os.FileMode(0644))
|
out += "[Service]\n"
|
||||||
if err != nil {
|
|
||||||
|
for key, val := range ec {
|
||||||
|
key = strings.ToUpper(key)
|
||||||
|
key = strings.Replace(key, "-", "_", -1)
|
||||||
|
|
||||||
|
if public != "" {
|
||||||
|
val = strings.Replace(val, "$public_ipv4", public, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if private != "" {
|
||||||
|
val = strings.Replace(val, "$private_ipv4", private, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
out += fmt.Sprintf("Environment=\"ETCD_%s=%s\"\n", key, val)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write an EtcdEnvironment to the appropriate path on disk for etcd.service
|
||||||
|
func WriteEtcdEnvironment(root string, env EtcdEnvironment) error {
|
||||||
|
cfgDir := path.Join(root, "etc", "systemd", "system", "etcd.service.d")
|
||||||
|
cfgFile := path.Join(cfgDir, "20-cloudinit.conf")
|
||||||
|
|
||||||
|
if _, err := os.Stat(cfgDir); err != nil {
|
||||||
|
if err := os.MkdirAll(cfgDir, os.FileMode(0755)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ioutil.WriteFile(etcdDiscoveryPath, []byte(url), os.FileMode(0644))
|
return ioutil.WriteFile(cfgFile, []byte(env.String()), os.FileMode(0644))
|
||||||
}
|
}
|
||||||
|
90
cloudinit/etcd_test.go
Normal file
90
cloudinit/etcd_test.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package cloudinit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEtcdEnvironment(t *testing.T) {
|
||||||
|
cfg := make(EtcdEnvironment, 0)
|
||||||
|
cfg["discovery_url"] = "http://disco.example.com/foobar"
|
||||||
|
cfg["peer-bind-addr"] = "127.0.0.1:7002"
|
||||||
|
|
||||||
|
env := cfg.String()
|
||||||
|
expect := `[Service]
|
||||||
|
Environment="ETCD_DISCOVERY_URL=http://disco.example.com/foobar"
|
||||||
|
Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
||||||
|
`
|
||||||
|
|
||||||
|
if env != expect {
|
||||||
|
t.Errorf("Generated environment:\n%s\nExpected environment:\n%s", env, expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdEnvironmentReplacement(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("COREOS_PUBLIC_IPV4", "203.0.113.29")
|
||||||
|
os.Setenv("COREOS_PRIVATE_IPV4", "192.0.2.13")
|
||||||
|
|
||||||
|
cfg := make(EtcdEnvironment, 0)
|
||||||
|
cfg["bind-addr"] = "$public_ipv4:4001"
|
||||||
|
cfg["peer-bind-addr"] = "$private_ipv4:7001"
|
||||||
|
|
||||||
|
env := cfg.String()
|
||||||
|
expect := `[Service]
|
||||||
|
Environment="ETCD_BIND_ADDR=203.0.113.29:4001"
|
||||||
|
Environment="ETCD_PEER_BIND_ADDR=192.0.2.13:7001"
|
||||||
|
`
|
||||||
|
if env != expect {
|
||||||
|
t.Errorf("Generated environment:\n%s\nExpected environment:\n%s", env, expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdEnvironmentWrittenToDisk(t *testing.T) {
|
||||||
|
ec := EtcdEnvironment{
|
||||||
|
"discovery_url": "http://disco.example.com/foobar",
|
||||||
|
"peer-bind-addr": "127.0.0.1:7002",
|
||||||
|
}
|
||||||
|
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to create tempdir: %v", err)
|
||||||
|
}
|
||||||
|
defer syscall.Rmdir(dir)
|
||||||
|
|
||||||
|
if err := WriteEtcdEnvironment(dir, ec); err != nil {
|
||||||
|
t.Fatalf("Processing of EtcdEnvironment failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath := path.Join(dir, "etc", "systemd", "system", "etcd.service.d", "20-cloudinit.conf")
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect := `[Service]
|
||||||
|
Environment="ETCD_DISCOVERY_URL=http://disco.example.com/foobar"
|
||||||
|
Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
||||||
|
`
|
||||||
|
if string(contents) != expect {
|
||||||
|
t.Fatalf("File has incorrect contents")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rmdir(path string) error {
|
||||||
|
cmd := exec.Command("rm", "-rf", path)
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user