feat(etcd): Write etcd systemd snippet
This commit is contained in:
parent
0841173dfc
commit
137949f5ad
@ -74,12 +74,38 @@ Provide a list of objects with the following attributes:
|
||||
|
||||
## 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
|
||||
[disco-service]: http://discovery.etcd.io
|
||||
For example, the following cloud-config document...
|
||||
|
||||
```
|
||||
#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
|
||||
|
||||
|
@ -12,7 +12,7 @@ const DefaultSSHKeyName = "coreos-cloudinit"
|
||||
type CloudConfig struct {
|
||||
SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"`
|
||||
Coreos struct {
|
||||
Etcd struct{ Discovery_URL string }
|
||||
Etcd EtcdEnvironment
|
||||
Fleet struct{ Autostart bool }
|
||||
Units []Unit
|
||||
}
|
||||
@ -98,13 +98,12 @@ func ApplyCloudConfig(cfg CloudConfig, sshKeyName string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Coreos.Etcd.Discovery_URL != "" {
|
||||
err := PersistEtcdDiscoveryURL(cfg.Coreos.Etcd.Discovery_URL)
|
||||
if err == nil {
|
||||
log.Printf("Consumed etcd discovery url")
|
||||
} else {
|
||||
log.Fatalf("Failed to persist etcd discovery url to filesystem: %v", err)
|
||||
if len(cfg.Coreos.Etcd) > 0 {
|
||||
if err := WriteEtcdEnvironment("/", cfg.Coreos.Etcd); err != nil {
|
||||
log.Fatalf("Failed to write etcd config to filesystem: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Wrote etcd config file to filesystem")
|
||||
}
|
||||
|
||||
if len(cfg.Coreos.Units) > 0 {
|
||||
|
@ -17,10 +17,6 @@ func TestCloudConfigEmpty(t *testing.T) {
|
||||
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 {
|
||||
t.Error("Expected AutostartFleet not to be defined")
|
||||
}
|
||||
@ -81,10 +77,6 @@ hostname: trontastic
|
||||
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 {
|
||||
t.Error("Expected AutostartFleet to be true")
|
||||
}
|
||||
|
@ -1,25 +1,48 @@
|
||||
package cloudinit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
etcdDiscoveryPath = "/var/run/etcd/bootstrap.disco"
|
||||
)
|
||||
type EtcdEnvironment map[string]string
|
||||
|
||||
func PersistEtcdDiscoveryURL(url string) error {
|
||||
dir := path.Dir(etcdDiscoveryPath)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
log.Printf("Creating directory /var/run/etcd")
|
||||
err := os.MkdirAll(dir, os.FileMode(0644))
|
||||
if err != nil {
|
||||
func (ec EtcdEnvironment) String() (out string) {
|
||||
public := os.Getenv("COREOS_PUBLIC_IPV4")
|
||||
private := os.Getenv("COREOS_PRIVATE_IPV4")
|
||||
|
||||
out += "[Service]\n"
|
||||
|
||||
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 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