2015-01-24 19:32:33 -08:00
|
|
|
// Copyright 2015 CoreOS, Inc.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2014-10-17 15:36:22 -07:00
|
|
|
|
2014-09-21 19:22:13 -07:00
|
|
|
package system
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/coreos/coreos-cloudinit/config"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
locksmithUnit = "locksmithd.service"
|
|
|
|
updateEngineUnit = "update-engine.service"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Update is a top-level structure which contains its underlying configuration,
|
|
|
|
// config.Update, a function for reading the configuration (the default
|
|
|
|
// implementation reading from the filesystem), and provides the system-specific
|
|
|
|
// File() and Unit().
|
|
|
|
type Update struct {
|
|
|
|
ReadConfig func() (io.Reader, error)
|
2014-10-23 11:44:15 -07:00
|
|
|
config.Update
|
2014-09-21 19:22:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func DefaultReadConfig() (io.Reader, error) {
|
|
|
|
etcUpdate := path.Join("/etc", "coreos", "update.conf")
|
|
|
|
usrUpdate := path.Join("/usr", "share", "coreos", "update.conf")
|
|
|
|
|
|
|
|
f, err := os.Open(etcUpdate)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
f, err = os.Open(usrUpdate)
|
|
|
|
}
|
|
|
|
return f, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 Update) File() (*File, error) {
|
2014-10-23 11:44:15 -07:00
|
|
|
if config.IsZero(uc.Update) {
|
2014-09-21 19:22:13 -07:00
|
|
|
return nil, nil
|
|
|
|
}
|
2014-10-28 20:04:43 -07:00
|
|
|
if err := config.AssertStructValid(uc.Update); err != nil {
|
2014-09-21 19:22:13 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the list of possible substitutions to be performed based on the options that are configured
|
|
|
|
subs := map[string]string{}
|
2014-10-23 11:44:15 -07:00
|
|
|
uct := reflect.TypeOf(uc.Update)
|
|
|
|
ucv := reflect.ValueOf(uc.Update)
|
2014-09-21 19:22:13 -07:00
|
|
|
for i := 0; i < uct.NumField(); i++ {
|
|
|
|
val := ucv.Field(i).String()
|
|
|
|
if val == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
env := uct.Field(i).Tag.Get("env")
|
|
|
|
subs[env] = fmt.Sprintf("%s=%s", env, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
conf, err := uc.ReadConfig()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
scanner := bufio.NewScanner(conf)
|
|
|
|
|
|
|
|
var out string
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := scanner.Text()
|
|
|
|
for env, value := range subs {
|
|
|
|
if strings.HasPrefix(line, env) {
|
|
|
|
line = value
|
|
|
|
delete(subs, env)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out += line
|
|
|
|
out += "\n"
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, key := range sortedKeys(subs) {
|
|
|
|
out += subs[key]
|
|
|
|
out += "\n"
|
|
|
|
}
|
|
|
|
|
2014-09-21 19:22:27 -07:00
|
|
|
return &File{config.File{
|
2014-09-21 19:22:13 -07:00
|
|
|
Path: path.Join("etc", "coreos", "update.conf"),
|
|
|
|
RawFilePermissions: "0644",
|
|
|
|
Content: out,
|
2014-09-21 19:22:27 -07:00
|
|
|
}}, nil
|
2014-09-21 19:22:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Units generates units for the cloud-init initializer to act on:
|
|
|
|
// - a locksmith Unit, if "reboot-strategy" was set in cloud-config
|
|
|
|
// - an update_engine Unit, if "group" or "server" was set in cloud-config
|
2014-10-06 15:14:29 -07:00
|
|
|
func (uc Update) Units() []Unit {
|
2014-09-21 19:22:13 -07:00
|
|
|
var units []Unit
|
2014-10-23 11:44:15 -07:00
|
|
|
if uc.Update.RebootStrategy != "" {
|
2014-09-21 19:22:38 -07:00
|
|
|
ls := &Unit{config.Unit{
|
2014-09-21 19:22:13 -07:00
|
|
|
Name: locksmithUnit,
|
|
|
|
Command: "restart",
|
|
|
|
Mask: false,
|
|
|
|
Runtime: true,
|
2014-09-21 19:22:38 -07:00
|
|
|
}}
|
2014-09-21 19:22:13 -07:00
|
|
|
|
2014-12-20 18:04:45 -08:00
|
|
|
if uc.Update.RebootStrategy == "off" {
|
2014-09-21 19:22:13 -07:00
|
|
|
ls.Command = "stop"
|
|
|
|
ls.Mask = true
|
|
|
|
}
|
|
|
|
units = append(units, *ls)
|
|
|
|
}
|
|
|
|
|
2014-10-23 11:44:15 -07:00
|
|
|
if uc.Update.Group != "" || uc.Update.Server != "" {
|
2014-09-21 19:22:38 -07:00
|
|
|
ue := Unit{config.Unit{
|
2014-09-21 19:22:13 -07:00
|
|
|
Name: updateEngineUnit,
|
|
|
|
Command: "restart",
|
2014-09-21 19:22:38 -07:00
|
|
|
}}
|
2014-09-21 19:22:13 -07:00
|
|
|
units = append(units, ue)
|
|
|
|
}
|
|
|
|
|
2014-10-06 15:14:29 -07:00
|
|
|
return units
|
2014-09-21 19:22:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func sortedKeys(m map[string]string) (keys []string) {
|
|
|
|
for key := range m {
|
|
|
|
keys = append(keys, key)
|
|
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
return
|
|
|
|
}
|