2014-05-07 04:44:18 +04:00
|
|
|
package initialize
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2014-05-14 23:22:10 +04:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2014-05-07 04:44:18 +04:00
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/coreos/coreos-cloudinit/system"
|
|
|
|
)
|
|
|
|
|
2014-05-14 23:22:10 +04:00
|
|
|
const (
|
|
|
|
locksmithUnit = "locksmithd.service"
|
|
|
|
)
|
2014-05-07 04:44:18 +04:00
|
|
|
|
2014-05-14 23:22:10 +04:00
|
|
|
// updateOption represents a configurable update option, which, if set, will be
|
|
|
|
// written into update.conf, replacing any existing value for the option
|
|
|
|
type updateOption struct {
|
|
|
|
key string // key used to configure this option in cloud-config
|
|
|
|
valid []string // valid values for the option
|
|
|
|
prefix string // prefix for the option in the update.conf file
|
|
|
|
value string // used to store the new value in update.conf (including prefix)
|
|
|
|
seen bool // whether the option has been seen in any existing update.conf
|
|
|
|
}
|
2014-05-07 04:44:18 +04:00
|
|
|
|
2014-05-14 23:22:10 +04:00
|
|
|
// updateOptions defines the update options understood by cloud-config.
|
|
|
|
// The keys represent the string used in cloud-config to configure the option.
|
|
|
|
var updateOptions = []*updateOption{
|
|
|
|
&updateOption{
|
|
|
|
key: "reboot-strategy",
|
|
|
|
prefix: "REBOOT_STRATEGY=",
|
|
|
|
valid: []string{"best-effort", "etcd-lock", "reboot", "off"},
|
|
|
|
},
|
|
|
|
&updateOption{
|
|
|
|
key: "group",
|
|
|
|
prefix: "GROUP=",
|
|
|
|
},
|
|
|
|
&updateOption{
|
|
|
|
key: "server",
|
|
|
|
prefix: "SERVER=",
|
|
|
|
},
|
2014-05-10 07:33:34 +04:00
|
|
|
}
|
2014-05-14 21:46:30 +04:00
|
|
|
|
2014-05-14 23:22:10 +04:00
|
|
|
// isValid checks whether a supplied value is valid for this option
|
|
|
|
func (uo updateOption) isValid(val string) bool {
|
|
|
|
if len(uo.valid) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, v := range uo.valid {
|
|
|
|
if val == v {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2014-05-10 07:33:34 +04:00
|
|
|
|
2014-05-14 23:22:10 +04:00
|
|
|
type UpdateConfig map[string]string
|
|
|
|
|
|
|
|
// 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 UpdateConfig) File(root string) (*system.File, error) {
|
|
|
|
if len(uc) < 1 {
|
2014-05-10 07:33:34 +04:00
|
|
|
return nil, nil
|
2014-05-07 04:44:18 +04:00
|
|
|
}
|
|
|
|
|
2014-05-10 07:33:34 +04:00
|
|
|
var out string
|
|
|
|
|
2014-05-14 23:22:10 +04:00
|
|
|
// Generate the list of possible substitutions to be performed based on the options that are configured
|
|
|
|
subs := make([]*updateOption, 0)
|
|
|
|
for _, uo := range updateOptions {
|
|
|
|
val, ok := uc[uo.key]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !uo.isValid(val) {
|
|
|
|
return nil, errors.New(fmt.Sprintf("invalid value %v for option %v (valid options: %v)", val, uo.key, uo.valid))
|
|
|
|
}
|
|
|
|
uo.value = uo.prefix + val
|
|
|
|
subs = append(subs, uo)
|
|
|
|
}
|
|
|
|
|
2014-05-10 07:33:34 +04:00
|
|
|
etcUpdate := path.Join(root, "etc", "coreos", "update.conf")
|
|
|
|
usrUpdate := path.Join(root, "usr", "share", "coreos", "update.conf")
|
|
|
|
|
2014-05-07 04:44:18 +04:00
|
|
|
conf, err := os.Open(etcUpdate)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
conf, err = os.Open(usrUpdate)
|
2014-05-10 07:33:34 +04:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2014-05-07 04:44:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(conf)
|
|
|
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := scanner.Text()
|
2014-05-14 23:22:10 +04:00
|
|
|
for _, s := range subs {
|
|
|
|
if strings.HasPrefix(line, s.prefix) {
|
|
|
|
line = s.value
|
|
|
|
s.seen = true
|
|
|
|
break
|
|
|
|
}
|
2014-05-07 04:44:18 +04:00
|
|
|
}
|
2014-05-10 07:33:34 +04:00
|
|
|
out += line
|
|
|
|
out += "\n"
|
2014-05-07 04:44:18 +04:00
|
|
|
if err := scanner.Err(); err != nil {
|
2014-05-10 07:33:34 +04:00
|
|
|
return nil, err
|
2014-05-07 04:44:18 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-14 23:22:10 +04:00
|
|
|
for _, s := range subs {
|
|
|
|
if !s.seen {
|
|
|
|
out += s.value
|
|
|
|
out += "\n"
|
|
|
|
}
|
2014-05-07 04:44:18 +04:00
|
|
|
}
|
2014-05-14 23:22:10 +04:00
|
|
|
|
2014-05-10 07:33:34 +04:00
|
|
|
return &system.File{
|
|
|
|
Path: path.Join("etc", "coreos", "update.conf"),
|
|
|
|
RawFilePermissions: "0644",
|
|
|
|
Content: out,
|
|
|
|
}, nil
|
2014-05-07 04:44:18 +04:00
|
|
|
}
|
|
|
|
|
2014-06-06 03:12:40 +04:00
|
|
|
// Units generates units for the cloud-init initializer to act on:
|
|
|
|
// - a locksmith system.Unit, if "reboot-strategy" was set in cloud-config
|
|
|
|
// - an update_engine system.Unit, if "group" was set in cloud-config
|
|
|
|
func (uc UpdateConfig) Units(root string) ([]system.Unit, error) {
|
|
|
|
var units []system.Unit
|
|
|
|
if strategy, ok := uc["reboot-strategy"]; ok {
|
|
|
|
ls := &system.Unit{
|
|
|
|
Name: locksmithUnit,
|
|
|
|
Command: "restart",
|
|
|
|
Mask: false,
|
2014-06-04 03:49:10 +04:00
|
|
|
Runtime: true,
|
2014-06-06 03:12:40 +04:00
|
|
|
}
|
2014-05-14 23:22:10 +04:00
|
|
|
|
2014-06-06 03:12:40 +04:00
|
|
|
if strategy == "off" {
|
|
|
|
ls.Command = "stop"
|
|
|
|
ls.Mask = true
|
|
|
|
}
|
|
|
|
units = append(units, *ls)
|
2014-05-07 04:44:18 +04:00
|
|
|
}
|
2014-05-10 07:33:34 +04:00
|
|
|
|
2014-06-06 03:12:40 +04:00
|
|
|
rue := false
|
|
|
|
if _, ok := uc["group"]; ok {
|
|
|
|
rue = true
|
|
|
|
}
|
|
|
|
if _, ok := uc["server"]; ok {
|
|
|
|
rue = true
|
|
|
|
}
|
|
|
|
if rue {
|
|
|
|
ue := system.Unit{
|
|
|
|
Name: "update-engine",
|
|
|
|
Command: "restart",
|
|
|
|
}
|
|
|
|
units = append(units, ue)
|
2014-05-07 04:44:18 +04:00
|
|
|
}
|
2014-05-10 07:33:34 +04:00
|
|
|
|
2014-06-06 03:12:40 +04:00
|
|
|
return units, nil
|
2014-05-07 04:44:18 +04:00
|
|
|
}
|