unit: refactor config

- Seperate the config from Destination()
- Add YAML tags for the fields
This commit is contained in:
Alex Crawford 2014-09-21 19:22:38 -07:00
parent 667dbd8fb7
commit 1fbbaaec19
11 changed files with 95 additions and 76 deletions

34
config/unit.go Normal file
View File

@ -0,0 +1,34 @@
package config
import (
"path/filepath"
"strings"
)
type Unit struct {
Name string `yaml:"name"`
Mask bool `yaml:"mask"`
Enable bool `yaml:"enable"`
Runtime bool `yaml:"runtime"`
Content string `yaml:"content"`
Command string `yaml:"command"`
// For drop-in units, a cloudinit.conf is generated.
// This is currently unbound in YAML (and hence unsettable in cloud-config files)
// until the correct behaviour for multiple drop-in units is determined.
DropIn bool `yaml:"-"`
}
func (u *Unit) Type() string {
ext := filepath.Ext(u.Name)
return strings.TrimLeft(ext, ".")
}
func (u *Unit) Group() string {
switch u.Type() {
case "network", "netdev", "link":
return "network"
default:
return "system"
}
}

View File

@ -35,7 +35,7 @@ type CloudConfig struct {
Fleet config.Fleet Fleet config.Fleet
OEM config.OEM OEM config.OEM
Update config.Update Update config.Update
Units []system.Unit Units []config.Unit
} }
WriteFiles []system.File `yaml:"write_files"` WriteFiles []system.File `yaml:"write_files"`
Hostname string Hostname string
@ -231,6 +231,11 @@ func Apply(cfg CloudConfig, env *Environment) error {
} }
} }
var units []system.Unit
for _, u := range cfg.Coreos.Units {
units = append(units, system.Unit{u})
}
for _, ccu := range []CloudConfigUnit{ for _, ccu := range []CloudConfigUnit{
system.Etcd{cfg.Coreos.Etcd}, system.Etcd{cfg.Coreos.Etcd},
system.Fleet{cfg.Coreos.Fleet}, system.Fleet{cfg.Coreos.Fleet},
@ -240,7 +245,7 @@ func Apply(cfg CloudConfig, env *Environment) error {
if err != nil { if err != nil {
return err return err
} }
cfg.Coreos.Units = append(cfg.Coreos.Units, u...) units = append(units, u...)
} }
wroteEnvironment := false wroteEnvironment := false
@ -291,7 +296,7 @@ func Apply(cfg CloudConfig, env *Environment) error {
} }
um := system.NewUnitManager(env.Root()) um := system.NewUnitManager(env.Root())
return processUnits(cfg.Coreos.Units, env.Root(), um) return processUnits(units, env.Root(), um)
} }

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/coreos/coreos-cloudinit/config"
"github.com/coreos/coreos-cloudinit/system" "github.com/coreos/coreos-cloudinit/system"
) )
@ -401,10 +402,10 @@ func (tum *TestUnitManager) UnmaskUnit(unit *system.Unit) error {
func TestProcessUnits(t *testing.T) { func TestProcessUnits(t *testing.T) {
tum := &TestUnitManager{} tum := &TestUnitManager{}
units := []system.Unit{ units := []system.Unit{
system.Unit{ system.Unit{config.Unit{
Name: "foo", Name: "foo",
Mask: true, Mask: true,
}, }},
} }
if err := processUnits(units, "", tum); err != nil { if err := processUnits(units, "", tum); err != nil {
t.Fatalf("unexpected error calling processUnits: %v", err) t.Fatalf("unexpected error calling processUnits: %v", err)
@ -415,9 +416,9 @@ func TestProcessUnits(t *testing.T) {
tum = &TestUnitManager{} tum = &TestUnitManager{}
units = []system.Unit{ units = []system.Unit{
system.Unit{ system.Unit{config.Unit{
Name: "bar.network", Name: "bar.network",
}, }},
} }
if err := processUnits(units, "", tum); err != nil { if err := processUnits(units, "", tum); err != nil {
t.Fatalf("unexpected error calling processUnits: %v", err) t.Fatalf("unexpected error calling processUnits: %v", err)
@ -428,10 +429,10 @@ func TestProcessUnits(t *testing.T) {
tum = &TestUnitManager{} tum = &TestUnitManager{}
units = []system.Unit{ units = []system.Unit{
system.Unit{ system.Unit{config.Unit{
Name: "baz.service", Name: "baz.service",
Content: "[Service]\nExecStart=/bin/true", Content: "[Service]\nExecStart=/bin/true",
}, }},
} }
if err := processUnits(units, "", tum); err != nil { if err := processUnits(units, "", tum); err != nil {
t.Fatalf("unexpected error calling processUnits: %v", err) t.Fatalf("unexpected error calling processUnits: %v", err)
@ -442,10 +443,10 @@ func TestProcessUnits(t *testing.T) {
tum = &TestUnitManager{} tum = &TestUnitManager{}
units = []system.Unit{ units = []system.Unit{
system.Unit{ system.Unit{config.Unit{
Name: "locksmithd.service", Name: "locksmithd.service",
Runtime: true, Runtime: true,
}, }},
} }
if err := processUnits(units, "", tum); err != nil { if err := processUnits(units, "", tum); err != nil {
t.Fatalf("unexpected error calling processUnits: %v", err) t.Fatalf("unexpected error calling processUnits: %v", err)
@ -456,10 +457,10 @@ func TestProcessUnits(t *testing.T) {
tum = &TestUnitManager{} tum = &TestUnitManager{}
units = []system.Unit{ units = []system.Unit{
system.Unit{ system.Unit{config.Unit{
Name: "woof", Name: "woof",
Enable: true, Enable: true,
}, }},
} }
if err := processUnits(units, "", tum); err != nil { if err := processUnits(units, "", tum); err != nil {
t.Fatalf("unexpected error calling processUnits: %v", err) t.Fatalf("unexpected error calling processUnits: %v", err)

View File

@ -16,10 +16,10 @@ func (ee Etcd) Units() ([]Unit, error) {
if content == "" { if content == "" {
return nil, nil return nil, nil
} }
return []Unit{{ return []Unit{{config.Unit{
Name: "etcd.service", Name: "etcd.service",
Runtime: true, Runtime: true,
DropIn: true, DropIn: true,
Content: content, Content: content,
}}, nil }}}, nil
} }

View File

@ -21,7 +21,7 @@ func TestEtcdUnits(t *testing.T) {
Discovery: "http://disco.example.com/foobar", Discovery: "http://disco.example.com/foobar",
PeerBindAddr: "127.0.0.1:7002", PeerBindAddr: "127.0.0.1:7002",
}, },
[]Unit{{ []Unit{{config.Unit{
Name: "etcd.service", Name: "etcd.service",
Runtime: true, Runtime: true,
DropIn: true, DropIn: true,
@ -29,7 +29,7 @@ func TestEtcdUnits(t *testing.T) {
Environment="ETCD_DISCOVERY=http://disco.example.com/foobar" Environment="ETCD_DISCOVERY=http://disco.example.com/foobar"
Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002" Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
`, `,
}}, }}},
}, },
{ {
config.Etcd{ config.Etcd{
@ -37,7 +37,7 @@ Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
Discovery: "http://disco.example.com/foobar", Discovery: "http://disco.example.com/foobar",
PeerBindAddr: "127.0.0.1:7002", PeerBindAddr: "127.0.0.1:7002",
}, },
[]Unit{{ []Unit{{config.Unit{
Name: "etcd.service", Name: "etcd.service",
Runtime: true, Runtime: true,
DropIn: true, DropIn: true,
@ -46,7 +46,7 @@ Environment="ETCD_DISCOVERY=http://disco.example.com/foobar"
Environment="ETCD_NAME=node001" Environment="ETCD_NAME=node001"
Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002" Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
`, `,
}}, }}},
}, },
} { } {
units, err := Etcd{tt.config}.Units() units, err := Etcd{tt.config}.Units()

View File

@ -17,10 +17,10 @@ func (fe Fleet) Units() ([]Unit, error) {
if content == "" { if content == "" {
return nil, nil return nil, nil
} }
return []Unit{{ return []Unit{{config.Unit{
Name: "fleet.service", Name: "fleet.service",
Runtime: true, Runtime: true,
DropIn: true, DropIn: true,
Content: content, Content: content,
}}, nil }}}, nil
} }

View File

@ -20,14 +20,14 @@ func TestFleetUnits(t *testing.T) {
config.Fleet{ config.Fleet{
PublicIP: "12.34.56.78", PublicIP: "12.34.56.78",
}, },
[]Unit{{ []Unit{{config.Unit{
Name: "fleet.service", Name: "fleet.service",
Content: `[Service] Content: `[Service]
Environment="FLEET_PUBLIC_IP=12.34.56.78" Environment="FLEET_PUBLIC_IP=12.34.56.78"
`, `,
Runtime: true, Runtime: true,
DropIn: true, DropIn: true,
}}, }}},
}, },
} { } {
units, err := Fleet{tt.config}.Units() units, err := Fleet{tt.config}.Units()

View File

@ -5,10 +5,12 @@ import (
"os" "os"
"path" "path"
"testing" "testing"
"github.com/coreos/coreos-cloudinit/config"
) )
func TestPlaceNetworkUnit(t *testing.T) { func TestPlaceNetworkUnit(t *testing.T) {
u := Unit{ u := Unit{config.Unit{
Name: "50-eth0.network", Name: "50-eth0.network",
Runtime: true, Runtime: true,
Content: `[Match] Content: `[Match]
@ -17,7 +19,7 @@ Name=eth47
[Network] [Network]
Address=10.209.171.177/19 Address=10.209.171.177/19
`, `,
} }}
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-") dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
if err != nil { if err != nil {
@ -66,10 +68,10 @@ func TestUnitDestination(t *testing.T) {
dir := "/some/dir" dir := "/some/dir"
name := "foobar.service" name := "foobar.service"
u := Unit{ u := Unit{config.Unit{
Name: name, Name: name,
DropIn: false, DropIn: false,
} }}
dst := u.Destination(dir) dst := u.Destination(dir)
expectDst := path.Join(dir, "etc", "systemd", "system", "foobar.service") expectDst := path.Join(dir, "etc", "systemd", "system", "foobar.service")
@ -87,14 +89,14 @@ func TestUnitDestination(t *testing.T) {
} }
func TestPlaceMountUnit(t *testing.T) { func TestPlaceMountUnit(t *testing.T) {
u := Unit{ u := Unit{config.Unit{
Name: "media-state.mount", Name: "media-state.mount",
Runtime: false, Runtime: false,
Content: `[Mount] Content: `[Mount]
What=/dev/sdb1 What=/dev/sdb1
Where=/media/state Where=/media/state
`, `,
} }}
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-") dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
if err != nil { if err != nil {
@ -162,7 +164,7 @@ func TestMaskUnit(t *testing.T) {
sd := &systemd{dir} sd := &systemd{dir}
// Ensure mask works with units that do not currently exist // Ensure mask works with units that do not currently exist
uf := &Unit{Name: "foo.service"} uf := &Unit{config.Unit{Name: "foo.service"}}
if err := sd.MaskUnit(uf); err != nil { if err := sd.MaskUnit(uf); err != nil {
t.Fatalf("Unable to mask new unit: %v", err) t.Fatalf("Unable to mask new unit: %v", err)
} }
@ -176,7 +178,7 @@ func TestMaskUnit(t *testing.T) {
} }
// Ensure mask works with unit files that already exist // Ensure mask works with unit files that already exist
ub := &Unit{Name: "bar.service"} ub := &Unit{config.Unit{Name: "bar.service"}}
barPath := path.Join(dir, "etc", "systemd", "system", "bar.service") barPath := path.Join(dir, "etc", "systemd", "system", "bar.service")
if _, err := os.Create(barPath); err != nil { if _, err := os.Create(barPath); err != nil {
t.Fatalf("Error creating new unit file: %v", err) t.Fatalf("Error creating new unit file: %v", err)
@ -202,12 +204,12 @@ func TestUnmaskUnit(t *testing.T) {
sd := &systemd{dir} sd := &systemd{dir}
nilUnit := &Unit{Name: "null.service"} nilUnit := &Unit{config.Unit{Name: "null.service"}}
if err := sd.UnmaskUnit(nilUnit); err != nil { if err := sd.UnmaskUnit(nilUnit); err != nil {
t.Errorf("unexpected error from unmasking nonexistent unit: %v", err) t.Errorf("unexpected error from unmasking nonexistent unit: %v", err)
} }
uf := &Unit{Name: "foo.service", Content: "[Service]\nExecStart=/bin/true"} uf := &Unit{config.Unit{Name: "foo.service", Content: "[Service]\nExecStart=/bin/true"}}
dst := uf.Destination(dir) dst := uf.Destination(dir)
if err := os.MkdirAll(path.Dir(dst), os.FileMode(0755)); err != nil { if err := os.MkdirAll(path.Dir(dst), os.FileMode(0755)); err != nil {
t.Fatalf("Unable to create unit directory: %v", err) t.Fatalf("Unable to create unit directory: %v", err)
@ -227,7 +229,7 @@ func TestUnmaskUnit(t *testing.T) {
t.Errorf("unmask of non-empty unit mutated unit contents unexpectedly") t.Errorf("unmask of non-empty unit mutated unit contents unexpectedly")
} }
ub := &Unit{Name: "bar.service"} ub := &Unit{config.Unit{Name: "bar.service"}}
dst = ub.Destination(dir) dst = ub.Destination(dir)
if err := os.Symlink("/dev/null", dst); err != nil { if err := os.Symlink("/dev/null", dst); err != nil {
t.Fatalf("Unable to create masked unit: %v", err) t.Fatalf("Unable to create masked unit: %v", err)

View File

@ -3,8 +3,8 @@ package system
import ( import (
"fmt" "fmt"
"path" "path"
"path/filepath"
"strings" "github.com/coreos/coreos-cloudinit/config"
) )
// Name for drop-in service configuration files created by cloudconfig // Name for drop-in service configuration files created by cloudconfig
@ -19,33 +19,10 @@ type UnitManager interface {
UnmaskUnit(unit *Unit) error UnmaskUnit(unit *Unit) error
} }
// Unit is a top-level structure which embeds its underlying configuration,
// config.Unit, and provides the system-specific Destination().
type Unit struct { type Unit struct {
Name string config.Unit
Mask bool
Enable bool
Runtime bool
Content string
Command string
// For drop-in units, a cloudinit.conf is generated.
// This is currently unbound in YAML (and hence unsettable in cloud-config files)
// until the correct behaviour for multiple drop-in units is determined.
DropIn bool `yaml:"-"`
}
func (u *Unit) Type() string {
ext := filepath.Ext(u.Name)
return strings.TrimLeft(ext, ".")
}
func (u *Unit) Group() (group string) {
t := u.Type()
if t == "network" || t == "netdev" || t == "link" {
group = "network"
} else {
group = "system"
}
return
} }
type Script []byte type Script []byte

View File

@ -103,12 +103,12 @@ func (uc Update) File() (*File, error) {
func (uc Update) Units() ([]Unit, error) { func (uc Update) Units() ([]Unit, error) {
var units []Unit var units []Unit
if uc.Config.RebootStrategy != "" { if uc.Config.RebootStrategy != "" {
ls := &Unit{ ls := &Unit{config.Unit{
Name: locksmithUnit, Name: locksmithUnit,
Command: "restart", Command: "restart",
Mask: false, Mask: false,
Runtime: true, Runtime: true,
} }}
if uc.Config.RebootStrategy == "off" { if uc.Config.RebootStrategy == "off" {
ls.Command = "stop" ls.Command = "stop"
@ -118,10 +118,10 @@ func (uc Update) Units() ([]Unit, error) {
} }
if uc.Config.Group != "" || uc.Config.Server != "" { if uc.Config.Group != "" || uc.Config.Server != "" {
ue := Unit{ ue := Unit{config.Unit{
Name: updateEngineUnit, Name: updateEngineUnit,
Command: "restart", Command: "restart",
} }}
units = append(units, ue) units = append(units, ue)
} }

View File

@ -27,43 +27,43 @@ func TestUpdateUnits(t *testing.T) {
}, },
{ {
config: config.Update{Group: "master", Server: "http://foo.com"}, config: config.Update{Group: "master", Server: "http://foo.com"},
units: []Unit{{ units: []Unit{{config.Unit{
Name: "update-engine.service", Name: "update-engine.service",
Command: "restart", Command: "restart",
}}, }}},
}, },
{ {
config: config.Update{RebootStrategy: "best-effort"}, config: config.Update{RebootStrategy: "best-effort"},
units: []Unit{{ units: []Unit{{config.Unit{
Name: "locksmithd.service", Name: "locksmithd.service",
Command: "restart", Command: "restart",
Runtime: true, Runtime: true,
}}, }}},
}, },
{ {
config: config.Update{RebootStrategy: "etcd-lock"}, config: config.Update{RebootStrategy: "etcd-lock"},
units: []Unit{{ units: []Unit{{config.Unit{
Name: "locksmithd.service", Name: "locksmithd.service",
Command: "restart", Command: "restart",
Runtime: true, Runtime: true,
}}, }}},
}, },
{ {
config: config.Update{RebootStrategy: "reboot"}, config: config.Update{RebootStrategy: "reboot"},
units: []Unit{{ units: []Unit{{config.Unit{
Name: "locksmithd.service", Name: "locksmithd.service",
Command: "restart", Command: "restart",
Runtime: true, Runtime: true,
}}, }}},
}, },
{ {
config: config.Update{RebootStrategy: "off"}, config: config.Update{RebootStrategy: "off"},
units: []Unit{{ units: []Unit{{config.Unit{
Name: "locksmithd.service", Name: "locksmithd.service",
Command: "stop", Command: "stop",
Runtime: true, Runtime: true,
Mask: true, Mask: true,
}}, }}},
}, },
} { } {
units, err := Update{tt.config, testReadConfig("")}.Units() units, err := Update{tt.config, testReadConfig("")}.Units()