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:
parent
420f7cf202
commit
ffc54b028c
@ -16,7 +16,7 @@ We've designed our implementation to allow the same cloud-config file to work ac
|
|||||||
|
|
||||||
The cloud-config file uses the [YAML][yaml] file format, which uses whitespace and new-lines to delimit lists, associative arrays, and values.
|
The cloud-config file uses the [YAML][yaml] file format, which uses whitespace and new-lines to delimit lists, associative arrays, and values.
|
||||||
|
|
||||||
A cloud-config file should contain `#cloud-config`, followed by an associative array which has zero or more of the following keys:
|
A cloud-config file must contain `#cloud-config`, followed by an associative array which has zero or more of the following keys:
|
||||||
|
|
||||||
- `coreos`
|
- `coreos`
|
||||||
- `ssh_authorized_keys`
|
- `ssh_authorized_keys`
|
||||||
@ -46,13 +46,13 @@ If the platform environment supports the templating feature of coreos-cloudinit
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
coreos:
|
coreos:
|
||||||
etcd:
|
etcd:
|
||||||
name: node001
|
name: node001
|
||||||
# generate a new token for each unique cluster from https://discovery.etcd.io/new
|
# generate a new token for each unique cluster from https://discovery.etcd.io/new
|
||||||
discovery: https://discovery.etcd.io/<token>
|
discovery: https://discovery.etcd.io/<token>
|
||||||
# multi-region and multi-cloud deployments need to use $public_ipv4
|
# multi-region and multi-cloud deployments need to use $public_ipv4
|
||||||
addr: $public_ipv4:4001
|
addr: $public_ipv4:4001
|
||||||
peer-addr: $private_ipv4:7001
|
peer-addr: $private_ipv4:7001
|
||||||
```
|
```
|
||||||
|
|
||||||
...will generate a systemd unit drop-in like this:
|
...will generate a systemd unit drop-in like this:
|
||||||
@ -66,7 +66,6 @@ Environment="ETCD_PEER_ADDR=192.0.2.13:7001"
|
|||||||
```
|
```
|
||||||
|
|
||||||
For more information about the available configuration parameters, see the [etcd documentation][etcd-config].
|
For more information about the available configuration parameters, see the [etcd documentation][etcd-config].
|
||||||
Note that hyphens in the coreos.etcd.* keys are mapped to underscores.
|
|
||||||
|
|
||||||
_Note: The `$private_ipv4` and `$public_ipv4` substitution variables referenced in other documents are only supported on Amazon EC2, Google Compute Engine, OpenStack, Rackspace, DigitalOcean, and Vagrant._
|
_Note: The `$private_ipv4` and `$public_ipv4` substitution variables referenced in other documents are only supported on Amazon EC2, Google Compute Engine, OpenStack, Rackspace, DigitalOcean, and Vagrant._
|
||||||
|
|
||||||
@ -80,9 +79,9 @@ The `coreos.fleet.*` parameters work very similarly to `coreos.etcd.*`, and allo
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
coreos:
|
coreos:
|
||||||
fleet:
|
fleet:
|
||||||
public-ip: $public_ipv4
|
public-ip: $public_ipv4
|
||||||
metadata: region=us-west
|
metadata: region=us-west
|
||||||
```
|
```
|
||||||
|
|
||||||
...will generate a systemd unit drop-in like this:
|
...will generate a systemd unit drop-in like this:
|
||||||
@ -105,8 +104,8 @@ The `coreos.flannel.*` parameters also work very similarly to `coreos.etcd.*` an
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
coreos:
|
coreos:
|
||||||
flannel:
|
flannel:
|
||||||
etcd-prefix: /coreos.com/network2
|
etcd-prefix: /coreos.com/network2
|
||||||
```
|
```
|
||||||
|
|
||||||
...will generate systemd unit drop-in like so:
|
...will generate systemd unit drop-in like so:
|
||||||
@ -158,6 +157,10 @@ Each item is an object with the following fields:
|
|||||||
- **content**: Plaintext string representing entire unit file. If no value is provided, the unit is assumed to exist already.
|
- **content**: Plaintext string representing entire unit file. If no value is provided, the unit is assumed to exist already.
|
||||||
- **command**: Command to execute on unit: start, stop, reload, restart, try-restart, reload-or-restart, reload-or-try-restart. The default behavior is to not execute any commands.
|
- **command**: Command to execute on unit: start, stop, reload, restart, try-restart, reload-or-restart, reload-or-try-restart. The default behavior is to not execute any commands.
|
||||||
- **mask**: Whether to mask the unit file by symlinking it to `/dev/null` (analogous to `systemctl mask <name>`). Note that unlike `systemctl mask`, **this will destructively remove any existing unit file** located at `/etc/systemd/system/<unit>`, to ensure that the mask succeeds. The default value is false.
|
- **mask**: Whether to mask the unit file by symlinking it to `/dev/null` (analogous to `systemctl mask <name>`). Note that unlike `systemctl mask`, **this will destructively remove any existing unit file** located at `/etc/systemd/system/<unit>`, to ensure that the mask succeeds. The default value is false.
|
||||||
|
- **drop-ins**: A list of unit drop-ins with the following fields:
|
||||||
|
- **name**: String representing unit's name. Required.
|
||||||
|
- **content**: Plaintext string representing entire file. Required.
|
||||||
|
|
||||||
|
|
||||||
**NOTE:** The command field is ignored for all network, netdev, and link units. The systemd-networkd.service unit will be restarted in their place.
|
**NOTE:** The command field is ignored for all network, netdev, and link units. The systemd-networkd.service unit will be restarted in their place.
|
||||||
|
|
||||||
@ -169,19 +172,34 @@ Write a unit to disk, automatically starting it.
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
coreos:
|
coreos:
|
||||||
units:
|
units:
|
||||||
- name: docker-redis.service
|
- name: docker-redis.service
|
||||||
command: start
|
command: start
|
||||||
content: |
|
content: |
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Redis container
|
Description=Redis container
|
||||||
Author=Me
|
Author=Me
|
||||||
After=docker.service
|
After=docker.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Restart=always
|
Restart=always
|
||||||
ExecStart=/usr/bin/docker start -a redis_server
|
ExecStart=/usr/bin/docker start -a redis_server
|
||||||
ExecStop=/usr/bin/docker stop -t 2 redis_server
|
ExecStop=/usr/bin/docker stop -t 2 redis_server
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the DOCKER_OPTS environment variable to docker.service.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
#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"'
|
||||||
```
|
```
|
||||||
|
|
||||||
Start the built-in `etcd` and `fleet` services:
|
Start the built-in `etcd` and `fleet` services:
|
||||||
@ -190,11 +208,11 @@ Start the built-in `etcd` and `fleet` services:
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
coreos:
|
coreos:
|
||||||
units:
|
units:
|
||||||
- name: etcd.service
|
- name: etcd.service
|
||||||
command: start
|
command: start
|
||||||
- name: fleet.service
|
- name: fleet.service
|
||||||
command: start
|
command: start
|
||||||
```
|
```
|
||||||
|
|
||||||
### ssh_authorized_keys
|
### ssh_authorized_keys
|
||||||
|
@ -335,26 +335,6 @@ func TestCloudConfigSerializationHeader(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestDropInIgnored asserts that users are unable to set DropIn=True on units
|
|
||||||
func TestDropInIgnored(t *testing.T) {
|
|
||||||
contents := `
|
|
||||||
coreos:
|
|
||||||
units:
|
|
||||||
- name: test
|
|
||||||
dropin: true
|
|
||||||
`
|
|
||||||
cfg, err := NewCloudConfig(contents)
|
|
||||||
if err != nil || len(cfg.Coreos.Units) != 1 {
|
|
||||||
t.Fatalf("Encountered unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if len(cfg.Coreos.Units) != 1 || cfg.Coreos.Units[0].Name != "test" {
|
|
||||||
t.Fatalf("Expected 1 unit, but got %d: %v", len(cfg.Coreos.Units), cfg.Coreos.Units)
|
|
||||||
}
|
|
||||||
if cfg.Coreos.Units[0].DropIn {
|
|
||||||
t.Errorf("dropin option on unit in cloud-config was not ignored!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloudConfigUsers(t *testing.T) {
|
func TestCloudConfigUsers(t *testing.T) {
|
||||||
contents := `
|
contents := `
|
||||||
users:
|
users:
|
||||||
|
@ -17,15 +17,16 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
type Unit struct {
|
type Unit struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Mask bool `yaml:"mask"`
|
Mask bool `yaml:"mask"`
|
||||||
Enable bool `yaml:"enable"`
|
Enable bool `yaml:"enable"`
|
||||||
Runtime bool `yaml:"runtime"`
|
Runtime bool `yaml:"runtime"`
|
||||||
Content string `yaml:"content"`
|
Content string `yaml:"content"`
|
||||||
Command string `yaml:"command" valid:"start,stop,restart,reload,try-restart,reload-or-restart,reload-or-try-restart"`
|
Command string `yaml:"command" valid:"start,stop,restart,reload,try-restart,reload-or-restart,reload-or-try-restart"`
|
||||||
|
DropIns []UnitDropIn `yaml:"drop_ins"`
|
||||||
// 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.
|
type UnitDropIn struct {
|
||||||
DropIn bool `yaml:"-"`
|
Name string `yaml:"name"`
|
||||||
|
Content string `yaml:"content"`
|
||||||
}
|
}
|
||||||
|
@ -201,6 +201,11 @@ func processUnits(units []system.Unit, root string, um system.UnitManager) error
|
|||||||
actions := make([]action, 0, len(units))
|
actions := make([]action, 0, len(units))
|
||||||
reload := false
|
reload := false
|
||||||
for _, unit := range units {
|
for _, unit := range units {
|
||||||
|
if unit.Name == "" {
|
||||||
|
log.Printf("Skipping unit without name")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if unit.Content != "" {
|
if unit.Content != "" {
|
||||||
log.Printf("Writing unit %q to filesystem", unit.Name)
|
log.Printf("Writing unit %q to filesystem", unit.Name)
|
||||||
if err := um.PlaceUnit(unit); err != nil {
|
if err := um.PlaceUnit(unit); err != nil {
|
||||||
@ -210,6 +215,17 @@ func processUnits(units []system.Unit, root string, um system.UnitManager) error
|
|||||||
reload = true
|
reload = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, dropin := range unit.DropIns {
|
||||||
|
if dropin.Name != "" && dropin.Content != "" {
|
||||||
|
log.Printf("Writing drop-in unit %q to filesystem", dropin.Name)
|
||||||
|
if err := um.PlaceUnitDropIn(unit, dropin); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("Wrote drop-in unit %q", dropin.Name)
|
||||||
|
reload = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if unit.Mask {
|
if unit.Mask {
|
||||||
log.Printf("Masking unit file %q", unit.Name)
|
log.Printf("Masking unit file %q", unit.Name)
|
||||||
if err := um.MaskUnit(unit); err != nil {
|
if err := um.MaskUnit(unit); err != nil {
|
||||||
|
@ -37,6 +37,10 @@ func (tum *TestUnitManager) PlaceUnit(u system.Unit) error {
|
|||||||
tum.placed = append(tum.placed, u.Name)
|
tum.placed = append(tum.placed, u.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (tum *TestUnitManager) PlaceUnitDropIn(u system.Unit, d config.UnitDropIn) error {
|
||||||
|
tum.placed = append(tum.placed, u.Name+".d/"+d.Name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (tum *TestUnitManager) EnableUnitFile(u system.Unit) error {
|
func (tum *TestUnitManager) EnableUnitFile(u system.Unit) error {
|
||||||
tum.enabled = append(tum.enabled, u.Name)
|
tum.enabled = append(tum.enabled, u.Name)
|
||||||
return nil
|
return nil
|
||||||
@ -122,6 +126,69 @@ func TestProcessUnits(t *testing.T) {
|
|||||||
enabled: []string{"woof"},
|
enabled: []string{"woof"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
units: []system.Unit{
|
||||||
|
system.Unit{Unit: config.Unit{
|
||||||
|
Name: "hi.service",
|
||||||
|
Runtime: true,
|
||||||
|
Content: "[Service]\nExecStart=/bin/echo hi",
|
||||||
|
DropIns: []config.UnitDropIn{
|
||||||
|
{
|
||||||
|
Name: "lo.conf",
|
||||||
|
Content: "[Service]\nExecStart=/bin/echo lo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bye.conf",
|
||||||
|
Content: "[Service]\nExecStart=/bin/echo bye",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
result: TestUnitManager{
|
||||||
|
placed: []string{"hi.service", "hi.service.d/lo.conf", "hi.service.d/bye.conf"},
|
||||||
|
unmasked: []string{"hi.service"},
|
||||||
|
reload: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
units: []system.Unit{
|
||||||
|
system.Unit{Unit: config.Unit{
|
||||||
|
DropIns: []config.UnitDropIn{
|
||||||
|
{
|
||||||
|
Name: "lo.conf",
|
||||||
|
Content: "[Service]\nExecStart=/bin/echo lo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
result: TestUnitManager{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
units: []system.Unit{
|
||||||
|
system.Unit{Unit: config.Unit{
|
||||||
|
Name: "hi.service",
|
||||||
|
DropIns: []config.UnitDropIn{
|
||||||
|
{
|
||||||
|
Content: "[Service]\nExecStart=/bin/echo lo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
result: TestUnitManager{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
units: []system.Unit{
|
||||||
|
system.Unit{Unit: config.Unit{
|
||||||
|
Name: "hi.service",
|
||||||
|
DropIns: []config.UnitDropIn{
|
||||||
|
{
|
||||||
|
Name: "lo.conf",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
result: TestUnitManager{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
|
|
||||||
// dropinContents generates the contents for a drop-in unit given the config.
|
// dropinContents generates the contents for a drop-in unit given the config.
|
||||||
// The argument must be a struct from the 'config' package.
|
// The argument must be a struct from the 'config' package.
|
||||||
func dropinContents(e interface{}) string {
|
func serviceContents(e interface{}) string {
|
||||||
et := reflect.TypeOf(e)
|
et := reflect.TypeOf(e)
|
||||||
ev := reflect.ValueOf(e)
|
ev := reflect.ValueOf(e)
|
||||||
|
|
||||||
@ -42,16 +42,3 @@ func dropinContents(e interface{}) string {
|
|||||||
}
|
}
|
||||||
return "[Service]\n" + out
|
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"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDropinContents(t *testing.T) {
|
func TestServiceContents(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Config interface{}
|
Config interface{}
|
||||||
Contents string
|
Contents string
|
||||||
@ -48,7 +48,7 @@ Environment="D=0.1"
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
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)
|
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.
|
// Units creates a Unit file drop-in for etcd, using any configured options.
|
||||||
func (ee Etcd) Units() []Unit {
|
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{},
|
config.Etcd{},
|
||||||
nil,
|
[]Unit{{config.Unit{
|
||||||
|
Name: "etcd.service",
|
||||||
|
Runtime: true,
|
||||||
|
DropIns: []config.UnitDropIn{{Name: "20-cloudinit.conf"}},
|
||||||
|
}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
config.Etcd{
|
config.Etcd{
|
||||||
@ -40,11 +44,13 @@ func TestEtcdUnits(t *testing.T) {
|
|||||||
[]Unit{{config.Unit{
|
[]Unit{{config.Unit{
|
||||||
Name: "etcd.service",
|
Name: "etcd.service",
|
||||||
Runtime: true,
|
Runtime: true,
|
||||||
DropIn: true,
|
DropIns: []config.UnitDropIn{{
|
||||||
Content: `[Service]
|
Name: "20-cloudinit.conf",
|
||||||
|
Content: `[Service]
|
||||||
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"
|
||||||
`,
|
`,
|
||||||
|
}},
|
||||||
}}},
|
}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -56,12 +62,14 @@ Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
|||||||
[]Unit{{config.Unit{
|
[]Unit{{config.Unit{
|
||||||
Name: "etcd.service",
|
Name: "etcd.service",
|
||||||
Runtime: true,
|
Runtime: true,
|
||||||
DropIn: true,
|
DropIns: []config.UnitDropIn{{
|
||||||
Content: `[Service]
|
Name: "20-cloudinit.conf",
|
||||||
|
Content: `[Service]
|
||||||
Environment="ETCD_DISCOVERY=http://disco.example.com/foobar"
|
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"
|
||||||
`,
|
`,
|
||||||
|
}},
|
||||||
}}},
|
}}},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
@ -13,5 +13,12 @@ type Flannel struct {
|
|||||||
// Units generates a Unit file drop-in for flannel, if any flannel options were
|
// Units generates a Unit file drop-in for flannel, if any flannel options were
|
||||||
// configured in cloud-config
|
// configured in cloud-config
|
||||||
func (fl Flannel) Units() []Unit {
|
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{},
|
config.Flannel{},
|
||||||
nil,
|
[]Unit{{config.Unit{
|
||||||
|
Name: "flanneld.service",
|
||||||
|
Runtime: true,
|
||||||
|
DropIns: []config.UnitDropIn{{Name: "20-cloudinit.conf"}},
|
||||||
|
}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
config.Flannel{
|
config.Flannel{
|
||||||
@ -22,13 +26,15 @@ func TestFlannelUnits(t *testing.T) {
|
|||||||
EtcdPrefix: "/coreos.com/network/tenant1",
|
EtcdPrefix: "/coreos.com/network/tenant1",
|
||||||
},
|
},
|
||||||
[]Unit{{config.Unit{
|
[]Unit{{config.Unit{
|
||||||
Name: "flanneld.service",
|
Name: "flanneld.service",
|
||||||
Content: `[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_ENDPOINT=http://12.34.56.78:4001"
|
||||||
Environment="FLANNELD_ETCD_PREFIX=/coreos.com/network/tenant1"
|
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
|
// Units generates a Unit file drop-in for fleet, if any fleet options were
|
||||||
// configured in cloud-config
|
// configured in cloud-config
|
||||||
func (fe Fleet) Units() []Unit {
|
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{},
|
config.Fleet{},
|
||||||
nil,
|
[]Unit{{config.Unit{
|
||||||
|
Name: "fleet.service",
|
||||||
|
Runtime: true,
|
||||||
|
DropIns: []config.UnitDropIn{{Name: "20-cloudinit.conf"}},
|
||||||
|
}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
config.Fleet{
|
config.Fleet{
|
||||||
PublicIP: "12.34.56.78",
|
PublicIP: "12.34.56.78",
|
||||||
},
|
},
|
||||||
[]Unit{{config.Unit{
|
[]Unit{{config.Unit{
|
||||||
Name: "fleet.service",
|
Name: "fleet.service",
|
||||||
Content: `[Service]
|
Runtime: true,
|
||||||
|
DropIns: []config.UnitDropIn{{
|
||||||
|
Name: "20-cloudinit.conf",
|
||||||
|
Content: `[Service]
|
||||||
Environment="FLEET_PUBLIC_IP=12.34.56.78"
|
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
|
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 {
|
func (s *systemd) EnableUnitFile(u Unit) error {
|
||||||
conn, err := dbus.New()
|
conn, err := dbus.New()
|
||||||
if err != nil {
|
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) {
|
func TestMachineID(t *testing.T) {
|
||||||
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -25,11 +25,9 @@ import (
|
|||||||
"github.com/coreos/coreos-cloudinit/config"
|
"github.com/coreos/coreos-cloudinit/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Name for drop-in service configuration files created by cloudconfig
|
|
||||||
const cloudConfigDropIn = "20-cloudinit.conf"
|
|
||||||
|
|
||||||
type UnitManager interface {
|
type UnitManager interface {
|
||||||
PlaceUnit(unit Unit) error
|
PlaceUnit(unit Unit) error
|
||||||
|
PlaceUnitDropIn(unit Unit, dropIn config.UnitDropIn) error
|
||||||
EnableUnitFile(unit Unit) error
|
EnableUnitFile(unit Unit) error
|
||||||
RunUnitCommand(unit Unit, command string) (string, error)
|
RunUnitCommand(unit Unit, command string) (string, error)
|
||||||
MaskUnit(unit Unit) 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
|
// argument indicates the effective base directory of the system (similar to a
|
||||||
// chroot).
|
// chroot).
|
||||||
func (u Unit) Destination(root string) string {
|
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"
|
dir := "etc"
|
||||||
if u.Runtime {
|
if u.Runtime {
|
||||||
dir = "run"
|
dir = "run"
|
||||||
}
|
}
|
||||||
|
return path.Join(root, dir, "systemd", u.Group())
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user