drop-in: add support for drop-ins
This allows a list of drop-ins for a unit to be declared inline within a cloud-config. For example: #cloud-config coreos: units: - name: docker.service drop-ins: - name: 50-insecure-registry.conf content: | [Service] Environment=DOCKER_OPTS='--insecure-registry="10.0.1.0/24"'
This commit is contained in:
@@ -25,7 +25,7 @@ import (
|
||||
|
||||
// dropinContents generates the contents for a drop-in unit given the config.
|
||||
// The argument must be a struct from the 'config' package.
|
||||
func dropinContents(e interface{}) string {
|
||||
func serviceContents(e interface{}) string {
|
||||
et := reflect.TypeOf(e)
|
||||
ev := reflect.ValueOf(e)
|
||||
|
||||
@@ -42,16 +42,3 @@ func dropinContents(e interface{}) string {
|
||||
}
|
||||
return "[Service]\n" + out
|
||||
}
|
||||
|
||||
func dropinFromConfig(cfg interface{}, name string) []Unit {
|
||||
content := dropinContents(cfg)
|
||||
if content == "" {
|
||||
return nil
|
||||
}
|
||||
return []Unit{{config.Unit{
|
||||
Name: name,
|
||||
Runtime: true,
|
||||
DropIn: true,
|
||||
Content: content,
|
||||
}}}
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDropinContents(t *testing.T) {
|
||||
func TestServiceContents(t *testing.T) {
|
||||
tests := []struct {
|
||||
Config interface{}
|
||||
Contents string
|
||||
@@ -48,7 +48,7 @@ Environment="D=0.1"
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if c := dropinContents(tt.Config); c != tt.Contents {
|
||||
if c := serviceContents(tt.Config); c != tt.Contents {
|
||||
t.Errorf("bad contents (%+v): want %q, got %q", tt, tt.Contents, c)
|
||||
}
|
||||
}
|
||||
|
@@ -28,5 +28,12 @@ type Etcd struct {
|
||||
|
||||
// Units creates a Unit file drop-in for etcd, using any configured options.
|
||||
func (ee Etcd) Units() []Unit {
|
||||
return dropinFromConfig(ee.Etcd, "etcd.service")
|
||||
return []Unit{{config.Unit{
|
||||
Name: "etcd.service",
|
||||
Runtime: true,
|
||||
DropIns: []config.UnitDropIn{{
|
||||
Name: "20-cloudinit.conf",
|
||||
Content: serviceContents(ee.Etcd),
|
||||
}},
|
||||
}}}
|
||||
}
|
||||
|
@@ -30,7 +30,11 @@ func TestEtcdUnits(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
config.Etcd{},
|
||||
nil,
|
||||
[]Unit{{config.Unit{
|
||||
Name: "etcd.service",
|
||||
Runtime: true,
|
||||
DropIns: []config.UnitDropIn{{Name: "20-cloudinit.conf"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
config.Etcd{
|
||||
@@ -40,11 +44,13 @@ func TestEtcdUnits(t *testing.T) {
|
||||
[]Unit{{config.Unit{
|
||||
Name: "etcd.service",
|
||||
Runtime: true,
|
||||
DropIn: true,
|
||||
Content: `[Service]
|
||||
DropIns: []config.UnitDropIn{{
|
||||
Name: "20-cloudinit.conf",
|
||||
Content: `[Service]
|
||||
Environment="ETCD_DISCOVERY=http://disco.example.com/foobar"
|
||||
Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
||||
`,
|
||||
}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
@@ -56,12 +62,14 @@ Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
||||
[]Unit{{config.Unit{
|
||||
Name: "etcd.service",
|
||||
Runtime: true,
|
||||
DropIn: true,
|
||||
Content: `[Service]
|
||||
DropIns: []config.UnitDropIn{{
|
||||
Name: "20-cloudinit.conf",
|
||||
Content: `[Service]
|
||||
Environment="ETCD_DISCOVERY=http://disco.example.com/foobar"
|
||||
Environment="ETCD_NAME=node001"
|
||||
Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
||||
`,
|
||||
}},
|
||||
}}},
|
||||
},
|
||||
} {
|
||||
|
@@ -13,5 +13,12 @@ type Flannel struct {
|
||||
// Units generates a Unit file drop-in for flannel, if any flannel options were
|
||||
// configured in cloud-config
|
||||
func (fl Flannel) Units() []Unit {
|
||||
return dropinFromConfig(fl.Flannel, "flanneld.service")
|
||||
return []Unit{{config.Unit{
|
||||
Name: "flanneld.service",
|
||||
Runtime: true,
|
||||
DropIns: []config.UnitDropIn{{
|
||||
Name: "20-cloudinit.conf",
|
||||
Content: serviceContents(fl.Flannel),
|
||||
}},
|
||||
}}}
|
||||
}
|
||||
|
@@ -14,7 +14,11 @@ func TestFlannelUnits(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
config.Flannel{},
|
||||
nil,
|
||||
[]Unit{{config.Unit{
|
||||
Name: "flanneld.service",
|
||||
Runtime: true,
|
||||
DropIns: []config.UnitDropIn{{Name: "20-cloudinit.conf"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
config.Flannel{
|
||||
@@ -22,13 +26,15 @@ func TestFlannelUnits(t *testing.T) {
|
||||
EtcdPrefix: "/coreos.com/network/tenant1",
|
||||
},
|
||||
[]Unit{{config.Unit{
|
||||
Name: "flanneld.service",
|
||||
Content: `[Service]
|
||||
Name: "flanneld.service",
|
||||
Runtime: true,
|
||||
DropIns: []config.UnitDropIn{{
|
||||
Name: "20-cloudinit.conf",
|
||||
Content: `[Service]
|
||||
Environment="FLANNELD_ETCD_ENDPOINT=http://12.34.56.78:4001"
|
||||
Environment="FLANNELD_ETCD_PREFIX=/coreos.com/network/tenant1"
|
||||
`,
|
||||
Runtime: true,
|
||||
DropIn: true,
|
||||
}},
|
||||
}}},
|
||||
},
|
||||
} {
|
||||
|
@@ -29,5 +29,12 @@ 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() []Unit {
|
||||
return dropinFromConfig(fe.Fleet, "fleet.service")
|
||||
return []Unit{{config.Unit{
|
||||
Name: "fleet.service",
|
||||
Runtime: true,
|
||||
DropIns: []config.UnitDropIn{{
|
||||
Name: "20-cloudinit.conf",
|
||||
Content: serviceContents(fe.Fleet),
|
||||
}},
|
||||
}}}
|
||||
}
|
||||
|
@@ -30,19 +30,25 @@ func TestFleetUnits(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
config.Fleet{},
|
||||
nil,
|
||||
[]Unit{{config.Unit{
|
||||
Name: "fleet.service",
|
||||
Runtime: true,
|
||||
DropIns: []config.UnitDropIn{{Name: "20-cloudinit.conf"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
config.Fleet{
|
||||
PublicIP: "12.34.56.78",
|
||||
},
|
||||
[]Unit{{config.Unit{
|
||||
Name: "fleet.service",
|
||||
Content: `[Service]
|
||||
Name: "fleet.service",
|
||||
Runtime: true,
|
||||
DropIns: []config.UnitDropIn{{
|
||||
Name: "20-cloudinit.conf",
|
||||
Content: `[Service]
|
||||
Environment="FLEET_PUBLIC_IP=12.34.56.78"
|
||||
`,
|
||||
Runtime: true,
|
||||
DropIn: true,
|
||||
}},
|
||||
}}},
|
||||
},
|
||||
} {
|
||||
|
@@ -54,6 +54,19 @@ func (s *systemd) PlaceUnit(u Unit) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// PlaceUnitDropIn writes a unit drop-in file at its desired destination,
|
||||
// creating parent directories as necessary.
|
||||
func (s *systemd) PlaceUnitDropIn(u Unit, d config.UnitDropIn) error {
|
||||
file := File{config.File{
|
||||
Path: u.DropInDestination(s.root, d),
|
||||
Content: d.Content,
|
||||
RawFilePermissions: "0644",
|
||||
}}
|
||||
|
||||
_, err := WriteFile(&file, "/")
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *systemd) EnableUnitFile(u Unit) error {
|
||||
conn, err := dbus.New()
|
||||
if err != nil {
|
||||
|
@@ -74,6 +74,64 @@ func TestPlaceUnit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlaceUnitDropIn(t *testing.T) {
|
||||
tests := []config.Unit{
|
||||
{
|
||||
Name: "false.service",
|
||||
Runtime: true,
|
||||
DropIns: []config.UnitDropIn{
|
||||
{
|
||||
Name: "00-true.conf",
|
||||
Content: "[Service]\nExecStart=\nExecStart=/usr/bin/true\n",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "true.service",
|
||||
DropIns: []config.UnitDropIn{
|
||||
{
|
||||
Name: "00-false.conf",
|
||||
Content: "[Service]\nExecStart=\nExecStart=/usr/bin/false\n",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Unable to create tempdir: %v", err))
|
||||
}
|
||||
|
||||
u := Unit{tt}
|
||||
sd := &systemd{dir}
|
||||
|
||||
if err := sd.PlaceUnitDropIn(u, u.DropIns[0]); err != nil {
|
||||
t.Fatalf("PlaceUnit(): bad error (%+v): want nil, got %s", tt, err)
|
||||
}
|
||||
|
||||
fi, err := os.Stat(u.DropInDestination(dir, u.DropIns[0]))
|
||||
if err != nil {
|
||||
t.Fatalf("Stat(): bad error (%+v): want nil, got %s", tt, err)
|
||||
}
|
||||
|
||||
if mode := fi.Mode(); mode != os.FileMode(0644) {
|
||||
t.Errorf("bad filemode (%+v): want %v, got %v", tt, os.FileMode(0644), mode)
|
||||
}
|
||||
|
||||
c, err := ioutil.ReadFile(u.DropInDestination(dir, u.DropIns[0]))
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile(): bad error (%+v): want nil, got %s", tt, err)
|
||||
}
|
||||
|
||||
if string(c) != u.DropIns[0].Content {
|
||||
t.Errorf("bad contents (%+v): want %q, got %q", tt, u.DropIns[0].Content, string(c))
|
||||
}
|
||||
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachineID(t *testing.T) {
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||
if err != nil {
|
||||
|
@@ -25,11 +25,9 @@ import (
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
)
|
||||
|
||||
// Name for drop-in service configuration files created by cloudconfig
|
||||
const cloudConfigDropIn = "20-cloudinit.conf"
|
||||
|
||||
type UnitManager interface {
|
||||
PlaceUnit(unit Unit) error
|
||||
PlaceUnitDropIn(unit Unit, dropIn config.UnitDropIn) error
|
||||
EnableUnitFile(unit Unit) error
|
||||
RunUnitCommand(unit Unit, command string) (string, error)
|
||||
MaskUnit(unit Unit) error
|
||||
@@ -66,14 +64,21 @@ func (u Unit) Group() string {
|
||||
// argument indicates the effective base directory of the system (similar to a
|
||||
// chroot).
|
||||
func (u Unit) Destination(root string) string {
|
||||
return path.Join(u.prefix(root), u.Name)
|
||||
}
|
||||
|
||||
// DropInDestination builds the appropriate absolute file path for the
|
||||
// UnitDropIn. The root argument indicates the effective base directory of the
|
||||
// system (similar to a chroot) and the dropIn argument is the UnitDropIn for
|
||||
// which the destination is being calculated.
|
||||
func (u Unit) DropInDestination(root string, dropIn config.UnitDropIn) string {
|
||||
return path.Join(u.prefix(root), fmt.Sprintf("%s.d", u.Name), dropIn.Name)
|
||||
}
|
||||
|
||||
func (u Unit) prefix(root string) string {
|
||||
dir := "etc"
|
||||
if u.Runtime {
|
||||
dir = "run"
|
||||
}
|
||||
|
||||
if u.DropIn {
|
||||
return path.Join(root, dir, "systemd", u.Group(), fmt.Sprintf("%s.d", u.Name), cloudConfigDropIn)
|
||||
} else {
|
||||
return path.Join(root, dir, "systemd", u.Group(), u.Name)
|
||||
}
|
||||
return path.Join(root, dir, "systemd", u.Group())
|
||||
}
|
||||
|
@@ -98,3 +98,41 @@ func TestDestination(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropInDestination(t *testing.T) {
|
||||
tests := []struct {
|
||||
root string
|
||||
unitName string
|
||||
dropInName string
|
||||
runtime bool
|
||||
|
||||
destination string
|
||||
}{
|
||||
{
|
||||
root: "/some/dir",
|
||||
unitName: "foo.service",
|
||||
dropInName: "bar.conf",
|
||||
destination: "/some/dir/etc/systemd/system/foo.service.d/bar.conf",
|
||||
},
|
||||
{
|
||||
root: "/some/dir",
|
||||
unitName: "foo.service",
|
||||
dropInName: "bar.conf",
|
||||
runtime: true,
|
||||
destination: "/some/dir/run/systemd/system/foo.service.d/bar.conf",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
u := Unit{config.Unit{
|
||||
Name: tt.unitName,
|
||||
Runtime: tt.runtime,
|
||||
DropIns: []config.UnitDropIn{{
|
||||
Name: tt.dropInName,
|
||||
}},
|
||||
}}
|
||||
if d := u.DropInDestination(tt.root, u.DropIns[0]); tt.destination != d {
|
||||
t.Errorf("bad destination (%+v): want %q, got %q", tt, tt.destination, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user