From 5981e12ac07648014bd12e1b5cff6a3665f4144d Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Mon, 14 Apr 2014 10:02:18 -0700 Subject: [PATCH 1/3] feat(unit): Allow user to control enabling units Fix #69 - A user may provide an `enable` attribute of a unit in their cloud config document. If true, coreos-cloudinit will instruct systemd to enable the associated unit. If false, the unit will not be enabled. Fix #71 - The default enable behavior has been changed from on to off. --- Documentation/cloud-config.md | 1 + initialize/config.go | 16 +++++++++------- system/systemd.go | 1 + 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Documentation/cloud-config.md b/Documentation/cloud-config.md index 6c4b516..a9aaf4f 100644 --- a/Documentation/cloud-config.md +++ b/Documentation/cloud-config.md @@ -87,6 +87,7 @@ Arbitrary systemd units may be provided in the `coreos.units` attribute. - **name**: String representing unit's name. Required. - **runtime**: Boolean indicating whether or not to persist the unit across reboots. This is analagous to the `--runtime` argument to `systemd enable`. Default value is false. +- **enable**: Boolean indicating whether or not to handle the [Install] section of the unit file. This is similar to running `systemctl enable `. Default value is false. - **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. Default value is restart. diff --git a/initialize/config.go b/initialize/config.go index 6213207..a861cea 100644 --- a/initialize/config.go +++ b/initialize/config.go @@ -140,14 +140,16 @@ func Apply(cfg CloudConfig, env *Environment) error { } log.Printf("Placed unit %s at %s", unit.Name, dst) - if unit.Group() != "network" { - log.Printf("Enabling unit file %s", dst) - if err := system.EnableUnitFile(dst, unit.Runtime); err != nil { - return err + if unit.Enable { + if unit.Group() != "network" { + log.Printf("Enabling unit file %s", dst) + if err := system.EnableUnitFile(dst, unit.Runtime); err != nil { + return err + } + log.Printf("Enabled unit %s", unit.Name) + } else { + log.Printf("Skipping enable for network-like unit %s", unit.Name) } - log.Printf("Enabled unit %s", unit.Name) - } else { - log.Printf("Skipping enable for network-like unit %s", unit.Name) } } diff --git a/system/systemd.go b/system/systemd.go index dda28b2..8f4d99b 100644 --- a/system/systemd.go +++ b/system/systemd.go @@ -19,6 +19,7 @@ const fakeMachineID = "42000000000000000000000000000042" type Unit struct { Name string + Enable bool Runtime bool Content string Command string From 476761cf62def98c02d4274054eb0e3a6bfb2016 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Mon, 14 Apr 2014 10:10:10 -0700 Subject: [PATCH 2/3] refactor(unit): Separate UnitDestination from PlaceUnit --- initialize/config.go | 6 +++--- system/systemd.go | 25 ++++++++++++++++--------- system/systemd_test.go | 38 ++++++++++++++++++++++++-------------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/initialize/config.go b/initialize/config.go index a861cea..a1b62d5 100644 --- a/initialize/config.go +++ b/initialize/config.go @@ -133,9 +133,9 @@ func Apply(cfg CloudConfig, env *Environment) error { for _, unit := range cfg.Coreos.Units { if unit.Content != "" { - log.Printf("Writing unit %s to filesystem", unit.Name) - dst, err := system.PlaceUnit(&unit, env.Root()) - if err != nil { + dst := system.UnitDestination(&unit, env.Root()) + log.Printf("Writing unit %s to filesystem at path %s", unit.Name, dst) + if err := system.PlaceUnit(&unit, dst); err != nil { return err } log.Printf("Placed unit %s at %s", unit.Name, dst) diff --git a/system/systemd.go b/system/systemd.go index 8f4d99b..9315638 100644 --- a/system/systemd.go +++ b/system/systemd.go @@ -42,21 +42,28 @@ func (u *Unit) Group() (group string) { type Script []byte -func PlaceUnit(u *Unit, root string) (string, error) { +// UnitDestination builds the appropriate absolte file path for +// the given unit. The root argument indicates the effective base +// directory of the system (similar to a chroot). +func UnitDestination(u *Unit, root string) string { dir := "etc" if u.Runtime { dir = "run" } - dst := path.Join(root, dir, "systemd", u.Group()) - if _, err := os.Stat(dst); os.IsNotExist(err) { - if err := os.MkdirAll(dst, os.FileMode(0755)); err != nil { - return "", err + return path.Join(root, dir, "systemd", u.Group(), u.Name) +} + +// PlaceUnit writes a unit file at the provided destination, creating +// parent directories as necessary. +func PlaceUnit(u *Unit, dst string) error { + dir := filepath.Dir(dst) + if _, err := os.Stat(dir); os.IsNotExist(err) { + if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil { + return err } } - dst = path.Join(dst, u.Name) - file := File{ Path: dst, Content: u.Content, @@ -65,10 +72,10 @@ func PlaceUnit(u *Unit, root string) (string, error) { err := WriteFile(&file) if err != nil { - return "", err + return err } - return dst, nil + return nil } func EnableUnitFile(file string, runtime bool) error { diff --git a/system/systemd_test.go b/system/systemd_test.go index 6635d0e..4498456 100644 --- a/system/systemd_test.go +++ b/system/systemd_test.go @@ -26,12 +26,17 @@ Address=10.209.171.177/19 } defer syscall.Rmdir(dir) - if _, err := PlaceUnit(&u, dir); err != nil { + dst := UnitDestination(&u, dir) + expectDst := path.Join(dir, "run", "systemd", "network", "50-eth0.network") + if dst != expectDst { + t.Fatalf("UnitDestination returned %s, expected %s", dst, expectDst) + } + + if err := PlaceUnit(&u, dst); err != nil { t.Fatalf("PlaceUnit failed: %v", err) } - fullPath := path.Join(dir, "run", "systemd", "network", "50-eth0.network") - fi, err := os.Stat(fullPath) + fi, err := os.Stat(dst) if err != nil { t.Fatalf("Unable to stat file: %v", err) } @@ -40,19 +45,19 @@ Address=10.209.171.177/19 t.Errorf("File has incorrect mode: %v", fi.Mode()) } - contents, err := ioutil.ReadFile(fullPath) + contents, err := ioutil.ReadFile(dst) if err != nil { t.Fatalf("Unable to read expected file: %v", err) } - expect := `[Match] + expectContents := `[Match] Name=eth47 [Network] Address=10.209.171.177/19 ` - if string(contents) != expect { - t.Fatalf("File has incorrect contents '%s'.\nExpected '%s'", string(contents), expect) + if string(contents) != expectContents { + t.Fatalf("File has incorrect contents '%s'.\nExpected '%s'", string(contents), expectContents) } } @@ -72,12 +77,17 @@ Where=/media/state } defer syscall.Rmdir(dir) - if _, err := PlaceUnit(&u, dir); err != nil { + dst := UnitDestination(&u, dir) + expectDst := path.Join(dir, "etc", "systemd", "system", "media-state.mount") + if dst != expectDst { + t.Fatalf("UnitDestination returned %s, expected %s", dst, expectDst) + } + + if err := PlaceUnit(&u, dst); err != nil { t.Fatalf("PlaceUnit failed: %v", err) } - fullPath := path.Join(dir, "etc", "systemd", "system", "media-state.mount") - fi, err := os.Stat(fullPath) + fi, err := os.Stat(dst) if err != nil { t.Fatalf("Unable to stat file: %v", err) } @@ -86,17 +96,17 @@ Where=/media/state t.Errorf("File has incorrect mode: %v", fi.Mode()) } - contents, err := ioutil.ReadFile(fullPath) + contents, err := ioutil.ReadFile(dst) if err != nil { t.Fatalf("Unable to read expected file: %v", err) } - expect := `[Mount] + expectContents := `[Mount] What=/dev/sdb1 Where=/media/state ` - if string(contents) != expect { - t.Fatalf("File has incorrect contents '%s'.\nExpected '%s'", string(contents), expect) + if string(contents) != expectContents { + t.Fatalf("File has incorrect contents '%s'.\nExpected '%s'", string(contents), expectContents) } } From 61bb63b6e6b47a32cff61d8ce0a3726764098dce Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 15 Apr 2014 09:00:19 -0700 Subject: [PATCH 3/3] feat(unit): Allow units to be enabled even if contents not provided --- initialize/config.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/initialize/config.go b/initialize/config.go index a1b62d5..71efefe 100644 --- a/initialize/config.go +++ b/initialize/config.go @@ -130,26 +130,25 @@ func Apply(cfg CloudConfig, env *Environment) error { if len(cfg.Coreos.Units) > 0 { commands := make(map[string]string, 0) - for _, unit := range cfg.Coreos.Units { + dst := system.UnitDestination(&unit, env.Root()) if unit.Content != "" { - dst := system.UnitDestination(&unit, env.Root()) log.Printf("Writing unit %s to filesystem at path %s", unit.Name, dst) if err := system.PlaceUnit(&unit, dst); err != nil { return err } log.Printf("Placed unit %s at %s", unit.Name, dst) + } - if unit.Enable { - if unit.Group() != "network" { - log.Printf("Enabling unit file %s", dst) - if err := system.EnableUnitFile(dst, unit.Runtime); err != nil { - return err - } - log.Printf("Enabled unit %s", unit.Name) - } else { - log.Printf("Skipping enable for network-like unit %s", unit.Name) + if unit.Enable { + if unit.Group() != "network" { + log.Printf("Enabling unit file %s", dst) + if err := system.EnableUnitFile(dst, unit.Runtime); err != nil { + return err } + log.Printf("Enabled unit %s", unit.Name) + } else { + log.Printf("Skipping enable for network-like unit %s", unit.Name) } }