update: refactor config
- Explicitly specify all of the valid options for Update - Seperate the config from File() and Units() - Add YAML tags for the fields
This commit is contained in:
48
system/etc_hosts.go
Normal file
48
system/etc_hosts.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
)
|
||||
|
||||
const DefaultIpv4Address = "127.0.0.1"
|
||||
|
||||
type EtcHosts struct {
|
||||
Config config.EtcHosts
|
||||
}
|
||||
|
||||
func (eh EtcHosts) generateEtcHosts() (out string, err error) {
|
||||
if eh.Config != "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\n", DefaultIpv4Address, hostname), nil
|
||||
|
||||
}
|
||||
|
||||
func (eh EtcHosts) File() (*File, error) {
|
||||
if eh.Config == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
etcHosts, err := eh.generateEtcHosts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &File{
|
||||
Path: path.Join("etc", "hosts"),
|
||||
RawFilePermissions: "0644",
|
||||
Content: etcHosts,
|
||||
}, nil
|
||||
}
|
46
system/etc_hosts_test.go
Normal file
46
system/etc_hosts_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
)
|
||||
|
||||
func TestEtcdHostsFile(t *testing.T) {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, tt := range []struct {
|
||||
config config.EtcHosts
|
||||
file *File
|
||||
err error
|
||||
}{
|
||||
{
|
||||
"invalid",
|
||||
nil,
|
||||
fmt.Errorf("Invalid option to manage_etc_hosts"),
|
||||
},
|
||||
{
|
||||
"localhost",
|
||||
&File{
|
||||
Content: fmt.Sprintf("127.0.0.1 %s\n", hostname),
|
||||
Path: "etc/hosts",
|
||||
RawFilePermissions: "0644",
|
||||
},
|
||||
nil,
|
||||
},
|
||||
} {
|
||||
file, err := EtcHosts{tt.config}.File()
|
||||
if !reflect.DeepEqual(tt.err, err) {
|
||||
t.Errorf("bad error (%q): want %q, got %q", tt.config, tt.err, err)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.file, file) {
|
||||
t.Errorf("bad units (%q): want %#v, got %#v", tt.config, tt.file, file)
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,7 +11,7 @@ type Etcd struct {
|
||||
}
|
||||
|
||||
// Units creates a Unit file drop-in for etcd, using any configured options.
|
||||
func (ee Etcd) Units(_ string) ([]Unit, error) {
|
||||
func (ee Etcd) Units() ([]Unit, error) {
|
||||
content := dropinContents(ee.Etcd)
|
||||
if content == "" {
|
||||
return nil, nil
|
||||
|
@@ -49,7 +49,7 @@ Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
||||
}},
|
||||
},
|
||||
} {
|
||||
units, err := Etcd{tt.config}.Units("")
|
||||
units, err := Etcd{tt.config}.Units()
|
||||
if err != nil {
|
||||
t.Errorf("bad error (%q): want %q, got %q", tt.config, nil, err)
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ type Fleet struct {
|
||||
|
||||
// Units generates a Unit file drop-in for fleet, if any fleet options were
|
||||
// configured in cloud-config
|
||||
func (fe Fleet) Units(_ string) ([]Unit, error) {
|
||||
func (fe Fleet) Units() ([]Unit, error) {
|
||||
content := dropinContents(fe.Fleet)
|
||||
if content == "" {
|
||||
return nil, nil
|
||||
|
@@ -30,7 +30,7 @@ Environment="FLEET_PUBLIC_IP=12.34.56.78"
|
||||
}},
|
||||
},
|
||||
} {
|
||||
units, err := Fleet{tt.config}.Units("")
|
||||
units, err := Fleet{tt.config}.Units()
|
||||
if err != nil {
|
||||
t.Errorf("bad error (%q): want %q, got %q", tt.config, nil, err)
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ type OEM struct {
|
||||
config.OEM
|
||||
}
|
||||
|
||||
func (oem OEM) File(_ string) (*File, error) {
|
||||
func (oem OEM) File() (*File, error) {
|
||||
if oem.ID == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ BUG_REPORT_URL="https://github.com/coreos/coreos-overlay"
|
||||
},
|
||||
},
|
||||
} {
|
||||
file, err := OEM{tt.config}.File("")
|
||||
file, err := OEM{tt.config}.File()
|
||||
if err != nil {
|
||||
t.Errorf("bad error (%q): want %q, got %q", tt.config, nil, err)
|
||||
}
|
||||
|
137
system/update.go
Normal file
137
system/update.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
)
|
||||
|
||||
const (
|
||||
locksmithUnit = "locksmithd.service"
|
||||
updateEngineUnit = "update-engine.service"
|
||||
)
|
||||
|
||||
// Update is a top-level structure which contains its underlying configuration,
|
||||
// config.Update, a function for reading the configuration (the default
|
||||
// implementation reading from the filesystem), and provides the system-specific
|
||||
// File() and Unit().
|
||||
type Update struct {
|
||||
Config config.Update
|
||||
ReadConfig func() (io.Reader, error)
|
||||
}
|
||||
|
||||
func DefaultReadConfig() (io.Reader, error) {
|
||||
etcUpdate := path.Join("/etc", "coreos", "update.conf")
|
||||
usrUpdate := path.Join("/usr", "share", "coreos", "update.conf")
|
||||
|
||||
f, err := os.Open(etcUpdate)
|
||||
if os.IsNotExist(err) {
|
||||
f, err = os.Open(usrUpdate)
|
||||
}
|
||||
return f, err
|
||||
}
|
||||
|
||||
// File generates an `/etc/coreos/update.conf` file (if any update
|
||||
// configuration options are set in cloud-config) by either rewriting the
|
||||
// existing file on disk, or starting from `/usr/share/coreos/update.conf`
|
||||
func (uc Update) File() (*File, error) {
|
||||
if config.IsZero(uc.Config) {
|
||||
return nil, nil
|
||||
}
|
||||
if err := config.AssertValid(uc.Config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate the list of possible substitutions to be performed based on the options that are configured
|
||||
subs := map[string]string{}
|
||||
uct := reflect.TypeOf(uc.Config)
|
||||
ucv := reflect.ValueOf(uc.Config)
|
||||
for i := 0; i < uct.NumField(); i++ {
|
||||
val := ucv.Field(i).String()
|
||||
if val == "" {
|
||||
continue
|
||||
}
|
||||
env := uct.Field(i).Tag.Get("env")
|
||||
subs[env] = fmt.Sprintf("%s=%s", env, val)
|
||||
}
|
||||
|
||||
conf, err := uc.ReadConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scanner := bufio.NewScanner(conf)
|
||||
|
||||
var out string
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
for env, value := range subs {
|
||||
if strings.HasPrefix(line, env) {
|
||||
line = value
|
||||
delete(subs, env)
|
||||
break
|
||||
}
|
||||
}
|
||||
out += line
|
||||
out += "\n"
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, key := range sortedKeys(subs) {
|
||||
out += subs[key]
|
||||
out += "\n"
|
||||
}
|
||||
|
||||
return &File{
|
||||
Path: path.Join("etc", "coreos", "update.conf"),
|
||||
RawFilePermissions: "0644",
|
||||
Content: out,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Units generates units for the cloud-init initializer to act on:
|
||||
// - a locksmith Unit, if "reboot-strategy" was set in cloud-config
|
||||
// - an update_engine Unit, if "group" or "server" was set in cloud-config
|
||||
func (uc Update) Units() ([]Unit, error) {
|
||||
var units []Unit
|
||||
if uc.Config.RebootStrategy != "" {
|
||||
ls := &Unit{
|
||||
Name: locksmithUnit,
|
||||
Command: "restart",
|
||||
Mask: false,
|
||||
Runtime: true,
|
||||
}
|
||||
|
||||
if uc.Config.RebootStrategy == "off" {
|
||||
ls.Command = "stop"
|
||||
ls.Mask = true
|
||||
}
|
||||
units = append(units, *ls)
|
||||
}
|
||||
|
||||
if uc.Config.Group != "" || uc.Config.Server != "" {
|
||||
ue := Unit{
|
||||
Name: updateEngineUnit,
|
||||
Command: "restart",
|
||||
}
|
||||
units = append(units, ue)
|
||||
}
|
||||
|
||||
return units, nil
|
||||
}
|
||||
|
||||
func sortedKeys(m map[string]string) (keys []string) {
|
||||
for key := range m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return
|
||||
}
|
151
system/update_test.go
Normal file
151
system/update_test.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
)
|
||||
|
||||
func testReadConfig(config string) func() (io.Reader, error) {
|
||||
return func() (io.Reader, error) {
|
||||
return strings.NewReader(config), nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUnits(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
config config.Update
|
||||
units []Unit
|
||||
err error
|
||||
}{
|
||||
{
|
||||
config: config.Update{},
|
||||
},
|
||||
{
|
||||
config: config.Update{Group: "master", Server: "http://foo.com"},
|
||||
units: []Unit{{
|
||||
Name: "update-engine.service",
|
||||
Command: "restart",
|
||||
}},
|
||||
},
|
||||
{
|
||||
config: config.Update{RebootStrategy: "best-effort"},
|
||||
units: []Unit{{
|
||||
Name: "locksmithd.service",
|
||||
Command: "restart",
|
||||
Runtime: true,
|
||||
}},
|
||||
},
|
||||
{
|
||||
config: config.Update{RebootStrategy: "etcd-lock"},
|
||||
units: []Unit{{
|
||||
Name: "locksmithd.service",
|
||||
Command: "restart",
|
||||
Runtime: true,
|
||||
}},
|
||||
},
|
||||
{
|
||||
config: config.Update{RebootStrategy: "reboot"},
|
||||
units: []Unit{{
|
||||
Name: "locksmithd.service",
|
||||
Command: "restart",
|
||||
Runtime: true,
|
||||
}},
|
||||
},
|
||||
{
|
||||
config: config.Update{RebootStrategy: "off"},
|
||||
units: []Unit{{
|
||||
Name: "locksmithd.service",
|
||||
Command: "stop",
|
||||
Runtime: true,
|
||||
Mask: true,
|
||||
}},
|
||||
},
|
||||
} {
|
||||
units, err := Update{tt.config, testReadConfig("")}.Units()
|
||||
if !reflect.DeepEqual(tt.err, err) {
|
||||
t.Errorf("bad error (%q): want %q, got %q", tt.config, tt.err, err)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.units, units) {
|
||||
t.Errorf("bad units (%q): want %#v, got %#v", tt.config, tt.units, units)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateFile(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
config config.Update
|
||||
orig string
|
||||
file *File
|
||||
err error
|
||||
}{
|
||||
{
|
||||
config: config.Update{},
|
||||
},
|
||||
{
|
||||
config: config.Update{RebootStrategy: "wizzlewazzle"},
|
||||
err: errors.New("invalid value \"wizzlewazzle\" for option \"RebootStrategy\" (valid options: \"best-effort,etcd-lock,reboot,off\")"),
|
||||
},
|
||||
{
|
||||
config: config.Update{Group: "master", Server: "http://foo.com"},
|
||||
file: &File{
|
||||
Content: "GROUP=master\nSERVER=http://foo.com\n",
|
||||
Path: "etc/coreos/update.conf",
|
||||
RawFilePermissions: "0644",
|
||||
},
|
||||
},
|
||||
{
|
||||
config: config.Update{RebootStrategy: "best-effort"},
|
||||
file: &File{
|
||||
Content: "REBOOT_STRATEGY=best-effort\n",
|
||||
Path: "etc/coreos/update.conf",
|
||||
RawFilePermissions: "0644",
|
||||
},
|
||||
},
|
||||
{
|
||||
config: config.Update{RebootStrategy: "etcd-lock"},
|
||||
file: &File{
|
||||
Content: "REBOOT_STRATEGY=etcd-lock\n",
|
||||
Path: "etc/coreos/update.conf",
|
||||
RawFilePermissions: "0644",
|
||||
},
|
||||
},
|
||||
{
|
||||
config: config.Update{RebootStrategy: "reboot"},
|
||||
file: &File{
|
||||
Content: "REBOOT_STRATEGY=reboot\n",
|
||||
Path: "etc/coreos/update.conf",
|
||||
RawFilePermissions: "0644",
|
||||
},
|
||||
},
|
||||
{
|
||||
config: config.Update{RebootStrategy: "off"},
|
||||
file: &File{
|
||||
Content: "REBOOT_STRATEGY=off\n",
|
||||
Path: "etc/coreos/update.conf",
|
||||
RawFilePermissions: "0644",
|
||||
},
|
||||
},
|
||||
{
|
||||
config: config.Update{RebootStrategy: "etcd-lock"},
|
||||
orig: "SERVER=https://example.com\nGROUP=thegroupc\nREBOOT_STRATEGY=awesome",
|
||||
file: &File{
|
||||
Content: "SERVER=https://example.com\nGROUP=thegroupc\nREBOOT_STRATEGY=etcd-lock\n",
|
||||
Path: "etc/coreos/update.conf",
|
||||
RawFilePermissions: "0644",
|
||||
},
|
||||
},
|
||||
} {
|
||||
file, err := Update{tt.config, testReadConfig(tt.orig)}.File()
|
||||
if !reflect.DeepEqual(tt.err, err) {
|
||||
t.Errorf("bad error (%q): want %q, got %q", tt.config, tt.err, err)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.file, file) {
|
||||
t.Errorf("bad units (%q): want %#v, got %#v", tt.config, tt.file, file)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user