Revert "Merge pull request #234 from crawford/validate"
This reverts commitcdfc94f4e9
, reversing changes made to2051cd3e1c
.
This commit is contained in:
@@ -6,7 +6,8 @@ import (
|
||||
"log"
|
||||
"path"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/gopkg.in/yaml.v1"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/network"
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
@@ -16,19 +17,146 @@ import (
|
||||
type CloudConfigFile interface {
|
||||
// File should either return (*system.File, error), or (nil, nil) if nothing
|
||||
// needs to be done for this configuration option.
|
||||
File() (*system.File, error)
|
||||
File(root string) (*system.File, error)
|
||||
}
|
||||
|
||||
// CloudConfigUnit represents a CoreOS specific configuration option that can generate
|
||||
// associated system.Units to be created/enabled appropriately
|
||||
type CloudConfigUnit interface {
|
||||
Units() ([]system.Unit, error)
|
||||
Units(root string) ([]system.Unit, error)
|
||||
}
|
||||
|
||||
// CloudConfig encapsulates the entire cloud-config configuration file and maps directly to YAML
|
||||
type CloudConfig struct {
|
||||
SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"`
|
||||
Coreos struct {
|
||||
Etcd EtcdEnvironment
|
||||
Fleet FleetEnvironment
|
||||
OEM OEMRelease
|
||||
Update UpdateConfig
|
||||
Units []system.Unit
|
||||
}
|
||||
WriteFiles []system.File `yaml:"write_files"`
|
||||
Hostname string
|
||||
Users []system.User
|
||||
ManageEtcHosts EtcHosts `yaml:"manage_etc_hosts"`
|
||||
NetworkConfigPath string
|
||||
NetworkConfig string
|
||||
}
|
||||
|
||||
type warner func(format string, v ...interface{})
|
||||
|
||||
// warnOnUnrecognizedKeys parses the contents of a cloud-config file and calls
|
||||
// warn(msg, key) for every unrecognized key (i.e. those not present in CloudConfig)
|
||||
func warnOnUnrecognizedKeys(contents string, warn warner) {
|
||||
// Generate a map of all understood cloud config options
|
||||
var cc map[string]interface{}
|
||||
b, _ := yaml.Marshal(&CloudConfig{})
|
||||
yaml.Unmarshal(b, &cc)
|
||||
|
||||
// Now unmarshal the entire provided contents
|
||||
var c map[string]interface{}
|
||||
yaml.Unmarshal([]byte(contents), &c)
|
||||
|
||||
// Check that every key in the contents exists in the cloud config
|
||||
for k, _ := range c {
|
||||
if _, ok := cc[k]; !ok {
|
||||
warn("Warning: unrecognized key %q in provided cloud config - ignoring section", k)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for unrecognized coreos options, if any are set
|
||||
if coreos, ok := c["coreos"]; ok {
|
||||
if set, ok := coreos.(map[interface{}]interface{}); ok {
|
||||
known := cc["coreos"].(map[interface{}]interface{})
|
||||
for k, _ := range set {
|
||||
if key, ok := k.(string); ok {
|
||||
if _, ok := known[key]; !ok {
|
||||
warn("Warning: unrecognized key %q in coreos section of provided cloud config - ignoring", key)
|
||||
}
|
||||
} else {
|
||||
warn("Warning: unrecognized key %q in coreos section of provided cloud config - ignoring", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any badly-specified users, if any are set
|
||||
if users, ok := c["users"]; ok {
|
||||
var known map[string]interface{}
|
||||
b, _ := yaml.Marshal(&system.User{})
|
||||
yaml.Unmarshal(b, &known)
|
||||
|
||||
if set, ok := users.([]interface{}); ok {
|
||||
for _, u := range set {
|
||||
if user, ok := u.(map[interface{}]interface{}); ok {
|
||||
for k, _ := range user {
|
||||
if key, ok := k.(string); ok {
|
||||
if _, ok := known[key]; !ok {
|
||||
warn("Warning: unrecognized key %q in user section of cloud config - ignoring", key)
|
||||
}
|
||||
} else {
|
||||
warn("Warning: unrecognized key %q in user section of cloud config - ignoring", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any badly-specified files, if any are set
|
||||
if files, ok := c["write_files"]; ok {
|
||||
var known map[string]interface{}
|
||||
b, _ := yaml.Marshal(&system.File{})
|
||||
yaml.Unmarshal(b, &known)
|
||||
|
||||
if set, ok := files.([]interface{}); ok {
|
||||
for _, f := range set {
|
||||
if file, ok := f.(map[interface{}]interface{}); ok {
|
||||
for k, _ := range file {
|
||||
if key, ok := k.(string); ok {
|
||||
if _, ok := known[key]; !ok {
|
||||
warn("Warning: unrecognized key %q in file section of cloud config - ignoring", key)
|
||||
}
|
||||
} else {
|
||||
warn("Warning: unrecognized key %q in file section of cloud config - ignoring", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewCloudConfig instantiates a new CloudConfig from the given contents (a
|
||||
// string of YAML), returning any error encountered. It will ignore unknown
|
||||
// fields but log encountering them.
|
||||
func NewCloudConfig(contents string) (*CloudConfig, error) {
|
||||
var cfg CloudConfig
|
||||
err := yaml.Unmarshal([]byte(contents), &cfg)
|
||||
if err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
warnOnUnrecognizedKeys(contents, log.Printf)
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func (cc CloudConfig) String() string {
|
||||
bytes, err := yaml.Marshal(cc)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
stringified := string(bytes)
|
||||
stringified = fmt.Sprintf("#cloud-config\n%s", stringified)
|
||||
|
||||
return stringified
|
||||
}
|
||||
|
||||
// Apply renders a CloudConfig to an Environment. This can involve things like
|
||||
// configuring the hostname, adding new users, writing various configuration
|
||||
// files to disk, and manipulating systemd services.
|
||||
func Apply(cfg config.CloudConfig, env *Environment) error {
|
||||
func Apply(cfg CloudConfig, env *Environment) error {
|
||||
if cfg.Hostname != "" {
|
||||
if err := system.SetHostname(cfg.Hostname); err != nil {
|
||||
return err
|
||||
@@ -88,44 +216,26 @@ func Apply(cfg config.CloudConfig, env *Environment) error {
|
||||
}
|
||||
}
|
||||
|
||||
var writeFiles []system.File
|
||||
for _, file := range cfg.WriteFiles {
|
||||
writeFiles = append(writeFiles, system.File{file})
|
||||
}
|
||||
|
||||
for _, ccf := range []CloudConfigFile{
|
||||
system.OEM{cfg.Coreos.OEM},
|
||||
system.Update{cfg.Coreos.Update, system.DefaultReadConfig},
|
||||
system.EtcHosts{cfg.ManageEtcHosts},
|
||||
} {
|
||||
f, err := ccf.File()
|
||||
for _, ccf := range []CloudConfigFile{cfg.Coreos.OEM, cfg.Coreos.Update, cfg.ManageEtcHosts} {
|
||||
f, err := ccf.File(env.Root())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f != nil {
|
||||
writeFiles = append(writeFiles, *f)
|
||||
cfg.WriteFiles = append(cfg.WriteFiles, *f)
|
||||
}
|
||||
}
|
||||
|
||||
var units []system.Unit
|
||||
for _, u := range cfg.Coreos.Units {
|
||||
units = append(units, system.Unit{u})
|
||||
}
|
||||
|
||||
for _, ccu := range []CloudConfigUnit{
|
||||
system.Etcd{cfg.Coreos.Etcd},
|
||||
system.Fleet{cfg.Coreos.Fleet},
|
||||
system.Update{cfg.Coreos.Update, system.DefaultReadConfig},
|
||||
} {
|
||||
u, err := ccu.Units()
|
||||
for _, ccu := range []CloudConfigUnit{cfg.Coreos.Etcd, cfg.Coreos.Fleet, cfg.Coreos.Update} {
|
||||
u, err := ccu.Units(env.Root())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
units = append(units, u...)
|
||||
cfg.Coreos.Units = append(cfg.Coreos.Units, u...)
|
||||
}
|
||||
|
||||
wroteEnvironment := false
|
||||
for _, file := range writeFiles {
|
||||
for _, file := range cfg.WriteFiles {
|
||||
fullPath, err := system.WriteFile(&file, env.Root())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -172,7 +282,7 @@ func Apply(cfg config.CloudConfig, env *Environment) error {
|
||||
}
|
||||
|
||||
um := system.NewUnitManager(env.Root())
|
||||
return processUnits(units, env.Root(), um)
|
||||
return processUnits(cfg.Coreos.Units, env.Root(), um)
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,12 +1,368 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
func TestCloudConfigInvalidKeys(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatalf("panic while instantiating CloudConfig with nil keys: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, tt := range []struct {
|
||||
contents string
|
||||
}{
|
||||
{"coreos:"},
|
||||
{"ssh_authorized_keys:"},
|
||||
{"ssh_authorized_keys:\n -"},
|
||||
{"ssh_authorized_keys:\n - 0:"},
|
||||
{"write_files:"},
|
||||
{"write_files:\n -"},
|
||||
{"write_files:\n - 0:"},
|
||||
{"users:"},
|
||||
{"users:\n -"},
|
||||
{"users:\n - 0:"},
|
||||
} {
|
||||
_, err := NewCloudConfig(tt.contents)
|
||||
if err != nil {
|
||||
t.Fatalf("error instantiating CloudConfig with invalid keys: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloudConfigUnknownKeys(t *testing.T) {
|
||||
contents := `
|
||||
coreos:
|
||||
etcd:
|
||||
discovery: "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877"
|
||||
coreos_unknown:
|
||||
foo: "bar"
|
||||
section_unknown:
|
||||
dunno:
|
||||
something
|
||||
bare_unknown:
|
||||
bar
|
||||
write_files:
|
||||
- content: fun
|
||||
path: /var/party
|
||||
file_unknown: nofun
|
||||
users:
|
||||
- name: fry
|
||||
passwd: somehash
|
||||
user_unknown: philip
|
||||
hostname:
|
||||
foo
|
||||
`
|
||||
cfg, err := NewCloudConfig(contents)
|
||||
if err != nil {
|
||||
t.Fatalf("error instantiating CloudConfig with unknown keys: %v", err)
|
||||
}
|
||||
if cfg.Hostname != "foo" {
|
||||
t.Fatalf("hostname not correctly set when invalid keys are present")
|
||||
}
|
||||
if len(cfg.Coreos.Etcd) < 1 {
|
||||
t.Fatalf("etcd section not correctly set when invalid keys are present")
|
||||
}
|
||||
if len(cfg.WriteFiles) < 1 || cfg.WriteFiles[0].Content != "fun" || cfg.WriteFiles[0].Path != "/var/party" {
|
||||
t.Fatalf("write_files section not correctly set when invalid keys are present")
|
||||
}
|
||||
if len(cfg.Users) < 1 || cfg.Users[0].Name != "fry" || cfg.Users[0].PasswordHash != "somehash" {
|
||||
t.Fatalf("users section not correctly set when invalid keys are present")
|
||||
}
|
||||
|
||||
var warnings string
|
||||
catchWarn := func(f string, v ...interface{}) {
|
||||
warnings += fmt.Sprintf(f, v...)
|
||||
}
|
||||
|
||||
warnOnUnrecognizedKeys(contents, catchWarn)
|
||||
|
||||
if !strings.Contains(warnings, "coreos_unknown") {
|
||||
t.Errorf("warnings did not catch unrecognized coreos option coreos_unknown")
|
||||
}
|
||||
if !strings.Contains(warnings, "bare_unknown") {
|
||||
t.Errorf("warnings did not catch unrecognized key bare_unknown")
|
||||
}
|
||||
if !strings.Contains(warnings, "section_unknown") {
|
||||
t.Errorf("warnings did not catch unrecognized key section_unknown")
|
||||
}
|
||||
if !strings.Contains(warnings, "user_unknown") {
|
||||
t.Errorf("warnings did not catch unrecognized user key user_unknown")
|
||||
}
|
||||
if !strings.Contains(warnings, "file_unknown") {
|
||||
t.Errorf("warnings did not catch unrecognized file key file_unknown")
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that the parsing of a cloud config file "generally works"
|
||||
func TestCloudConfigEmpty(t *testing.T) {
|
||||
cfg, err := NewCloudConfig("")
|
||||
if err != nil {
|
||||
t.Fatalf("Encountered unexpected error :%v", err)
|
||||
}
|
||||
|
||||
keys := cfg.SSHAuthorizedKeys
|
||||
if len(keys) != 0 {
|
||||
t.Error("Parsed incorrect number of SSH keys")
|
||||
}
|
||||
|
||||
if len(cfg.WriteFiles) != 0 {
|
||||
t.Error("Expected zero WriteFiles")
|
||||
}
|
||||
|
||||
if cfg.Hostname != "" {
|
||||
t.Errorf("Expected hostname to be empty, got '%s'", cfg.Hostname)
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that the parsing of a cloud config file "generally works"
|
||||
func TestCloudConfig(t *testing.T) {
|
||||
contents := `
|
||||
coreos:
|
||||
etcd:
|
||||
discovery: "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877"
|
||||
update:
|
||||
reboot-strategy: reboot
|
||||
units:
|
||||
- name: 50-eth0.network
|
||||
runtime: yes
|
||||
content: '[Match]
|
||||
|
||||
Name=eth47
|
||||
|
||||
|
||||
[Network]
|
||||
|
||||
Address=10.209.171.177/19
|
||||
|
||||
'
|
||||
oem:
|
||||
id: rackspace
|
||||
name: Rackspace Cloud Servers
|
||||
version-id: 168.0.0
|
||||
home-url: https://www.rackspace.com/cloud/servers/
|
||||
bug-report-url: https://github.com/coreos/coreos-overlay
|
||||
ssh_authorized_keys:
|
||||
- foobar
|
||||
- foobaz
|
||||
write_files:
|
||||
- content: |
|
||||
penny
|
||||
elroy
|
||||
path: /etc/dogepack.conf
|
||||
permissions: '0644'
|
||||
owner: root:dogepack
|
||||
hostname: trontastic
|
||||
`
|
||||
cfg, err := NewCloudConfig(contents)
|
||||
if err != nil {
|
||||
t.Fatalf("Encountered unexpected error :%v", err)
|
||||
}
|
||||
|
||||
keys := cfg.SSHAuthorizedKeys
|
||||
if len(keys) != 2 {
|
||||
t.Error("Parsed incorrect number of SSH keys")
|
||||
} else if keys[0] != "foobar" {
|
||||
t.Error("Expected first SSH key to be 'foobar'")
|
||||
} else if keys[1] != "foobaz" {
|
||||
t.Error("Expected first SSH key to be 'foobaz'")
|
||||
}
|
||||
|
||||
if len(cfg.WriteFiles) != 1 {
|
||||
t.Error("Failed to parse correct number of write_files")
|
||||
} else {
|
||||
wf := cfg.WriteFiles[0]
|
||||
if wf.Content != "penny\nelroy\n" {
|
||||
t.Errorf("WriteFile has incorrect contents '%s'", wf.Content)
|
||||
}
|
||||
if wf.Encoding != "" {
|
||||
t.Errorf("WriteFile has incorrect encoding %s", wf.Encoding)
|
||||
}
|
||||
if perm, _ := wf.Permissions(); perm != 0644 {
|
||||
t.Errorf("WriteFile has incorrect permissions %s", perm)
|
||||
}
|
||||
if wf.Path != "/etc/dogepack.conf" {
|
||||
t.Errorf("WriteFile has incorrect path %s", wf.Path)
|
||||
}
|
||||
if wf.Owner != "root:dogepack" {
|
||||
t.Errorf("WriteFile has incorrect owner %s", wf.Owner)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cfg.Coreos.Units) != 1 {
|
||||
t.Error("Failed to parse correct number of units")
|
||||
} else {
|
||||
u := cfg.Coreos.Units[0]
|
||||
expect := `[Match]
|
||||
Name=eth47
|
||||
|
||||
[Network]
|
||||
Address=10.209.171.177/19
|
||||
`
|
||||
if u.Content != expect {
|
||||
t.Errorf("Unit has incorrect contents '%s'.\nExpected '%s'.", u.Content, expect)
|
||||
}
|
||||
if u.Runtime != true {
|
||||
t.Errorf("Unit has incorrect runtime value")
|
||||
}
|
||||
if u.Name != "50-eth0.network" {
|
||||
t.Errorf("Unit has incorrect name %s", u.Name)
|
||||
}
|
||||
if u.Type() != "network" {
|
||||
t.Errorf("Unit has incorrect type '%s'", u.Type())
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Coreos.OEM.ID != "rackspace" {
|
||||
t.Errorf("Failed parsing coreos.oem. Expected ID 'rackspace', got %q.", cfg.Coreos.OEM.ID)
|
||||
}
|
||||
|
||||
if cfg.Hostname != "trontastic" {
|
||||
t.Errorf("Failed to parse hostname")
|
||||
}
|
||||
if cfg.Coreos.Update["reboot-strategy"] != "reboot" {
|
||||
t.Errorf("Failed to parse locksmith strategy")
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that our interface conversion doesn't panic
|
||||
func TestCloudConfigKeysNotList(t *testing.T) {
|
||||
contents := `
|
||||
ssh_authorized_keys:
|
||||
- foo: bar
|
||||
`
|
||||
cfg, err := NewCloudConfig(contents)
|
||||
if err != nil {
|
||||
t.Fatalf("Encountered unexpected error: %v", err)
|
||||
}
|
||||
|
||||
keys := cfg.SSHAuthorizedKeys
|
||||
if len(keys) != 0 {
|
||||
t.Error("Parsed incorrect number of SSH keys")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloudConfigSerializationHeader(t *testing.T) {
|
||||
cfg, _ := NewCloudConfig("")
|
||||
contents := cfg.String()
|
||||
header := strings.SplitN(contents, "\n", 2)[0]
|
||||
if header != "#cloud-config" {
|
||||
t.Fatalf("Serialized config did not have expected header")
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
contents := `
|
||||
users:
|
||||
- name: elroy
|
||||
passwd: somehash
|
||||
ssh-authorized-keys:
|
||||
- somekey
|
||||
gecos: arbitrary comment
|
||||
homedir: /home/place
|
||||
no-create-home: yes
|
||||
primary-group: things
|
||||
groups:
|
||||
- ping
|
||||
- pong
|
||||
no-user-group: true
|
||||
system: y
|
||||
no-log-init: True
|
||||
`
|
||||
cfg, err := NewCloudConfig(contents)
|
||||
if err != nil {
|
||||
t.Fatalf("Encountered unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Users) != 1 {
|
||||
t.Fatalf("Parsed %d users, expected 1", cfg.Users)
|
||||
}
|
||||
|
||||
user := cfg.Users[0]
|
||||
|
||||
if user.Name != "elroy" {
|
||||
t.Errorf("User name is %q, expected 'elroy'", user.Name)
|
||||
}
|
||||
|
||||
if user.PasswordHash != "somehash" {
|
||||
t.Errorf("User passwd is %q, expected 'somehash'", user.PasswordHash)
|
||||
}
|
||||
|
||||
if keys := user.SSHAuthorizedKeys; len(keys) != 1 {
|
||||
t.Errorf("Parsed %d ssh keys, expected 1", len(keys))
|
||||
} else {
|
||||
key := user.SSHAuthorizedKeys[0]
|
||||
if key != "somekey" {
|
||||
t.Errorf("User SSH key is %q, expected 'somekey'", key)
|
||||
}
|
||||
}
|
||||
|
||||
if user.GECOS != "arbitrary comment" {
|
||||
t.Errorf("Failed to parse gecos field, got %q", user.GECOS)
|
||||
}
|
||||
|
||||
if user.Homedir != "/home/place" {
|
||||
t.Errorf("Failed to parse homedir field, got %q", user.Homedir)
|
||||
}
|
||||
|
||||
if !user.NoCreateHome {
|
||||
t.Errorf("Failed to parse no-create-home field")
|
||||
}
|
||||
|
||||
if user.PrimaryGroup != "things" {
|
||||
t.Errorf("Failed to parse primary-group field, got %q", user.PrimaryGroup)
|
||||
}
|
||||
|
||||
if len(user.Groups) != 2 {
|
||||
t.Errorf("Failed to parse 2 goups, got %d", len(user.Groups))
|
||||
} else {
|
||||
if user.Groups[0] != "ping" {
|
||||
t.Errorf("First group was %q, not expected value 'ping'", user.Groups[0])
|
||||
}
|
||||
if user.Groups[1] != "pong" {
|
||||
t.Errorf("First group was %q, not expected value 'pong'", user.Groups[1])
|
||||
}
|
||||
}
|
||||
|
||||
if !user.NoUserGroup {
|
||||
t.Errorf("Failed to parse no-user-group field")
|
||||
}
|
||||
|
||||
if !user.System {
|
||||
t.Errorf("Failed to parse system field")
|
||||
}
|
||||
|
||||
if !user.NoLogInit {
|
||||
t.Errorf("Failed to parse no-log-init field")
|
||||
}
|
||||
}
|
||||
|
||||
type TestUnitManager struct {
|
||||
placed []string
|
||||
enabled []string
|
||||
@@ -45,10 +401,10 @@ func (tum *TestUnitManager) UnmaskUnit(unit *system.Unit) error {
|
||||
func TestProcessUnits(t *testing.T) {
|
||||
tum := &TestUnitManager{}
|
||||
units := []system.Unit{
|
||||
system.Unit{config.Unit{
|
||||
system.Unit{
|
||||
Name: "foo",
|
||||
Mask: true,
|
||||
}},
|
||||
},
|
||||
}
|
||||
if err := processUnits(units, "", tum); err != nil {
|
||||
t.Fatalf("unexpected error calling processUnits: %v", err)
|
||||
@@ -59,9 +415,9 @@ func TestProcessUnits(t *testing.T) {
|
||||
|
||||
tum = &TestUnitManager{}
|
||||
units = []system.Unit{
|
||||
system.Unit{config.Unit{
|
||||
system.Unit{
|
||||
Name: "bar.network",
|
||||
}},
|
||||
},
|
||||
}
|
||||
if err := processUnits(units, "", tum); err != nil {
|
||||
t.Fatalf("unexpected error calling processUnits: %v", err)
|
||||
@@ -72,10 +428,10 @@ func TestProcessUnits(t *testing.T) {
|
||||
|
||||
tum = &TestUnitManager{}
|
||||
units = []system.Unit{
|
||||
system.Unit{config.Unit{
|
||||
system.Unit{
|
||||
Name: "baz.service",
|
||||
Content: "[Service]\nExecStart=/bin/true",
|
||||
}},
|
||||
},
|
||||
}
|
||||
if err := processUnits(units, "", tum); err != nil {
|
||||
t.Fatalf("unexpected error calling processUnits: %v", err)
|
||||
@@ -86,10 +442,10 @@ func TestProcessUnits(t *testing.T) {
|
||||
|
||||
tum = &TestUnitManager{}
|
||||
units = []system.Unit{
|
||||
system.Unit{config.Unit{
|
||||
system.Unit{
|
||||
Name: "locksmithd.service",
|
||||
Runtime: true,
|
||||
}},
|
||||
},
|
||||
}
|
||||
if err := processUnits(units, "", tum); err != nil {
|
||||
t.Fatalf("unexpected error calling processUnits: %v", err)
|
||||
@@ -100,10 +456,10 @@ func TestProcessUnits(t *testing.T) {
|
||||
|
||||
tum = &TestUnitManager{}
|
||||
units = []system.Unit{
|
||||
system.Unit{config.Unit{
|
||||
system.Unit{
|
||||
Name: "woof",
|
||||
Enable: true,
|
||||
}},
|
||||
},
|
||||
}
|
||||
if err := processUnits(units, "", tum); err != nil {
|
||||
t.Fatalf("unexpected error calling processUnits: %v", err)
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
@@ -82,9 +81,9 @@ func (e *Environment) Apply(data string) string {
|
||||
|
||||
func (e *Environment) DefaultEnvironmentFile() *system.EnvFile {
|
||||
ef := system.EnvFile{
|
||||
File: &system.File{config.File{
|
||||
File: &system.File{
|
||||
Path: "/etc/environment",
|
||||
}},
|
||||
},
|
||||
Vars: map[string]string{},
|
||||
}
|
||||
if ip, ok := e.substitutions["$public_ipv4"]; ok && len(ip) > 0 {
|
||||
@@ -105,3 +104,16 @@ func (e *Environment) DefaultEnvironmentFile() *system.EnvFile {
|
||||
return &ef
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeSvcEnv standardizes the keys of the map (environment variables for a service)
|
||||
// by replacing any dashes with underscores and ensuring they are entirely upper case.
|
||||
// For example, "some-env" --> "SOME_ENV"
|
||||
func normalizeSvcEnv(m map[string]string) map[string]string {
|
||||
out := make(map[string]string, len(m))
|
||||
for key, val := range m {
|
||||
key = strings.ToUpper(key)
|
||||
key = strings.Replace(key, "-", "_", -1)
|
||||
out[key] = val
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
63
initialize/etcd.go
Normal file
63
initialize/etcd.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
type EtcdEnvironment map[string]string
|
||||
|
||||
func (ee EtcdEnvironment) String() (out string) {
|
||||
norm := normalizeSvcEnv(ee)
|
||||
|
||||
if val, ok := norm["DISCOVERY_URL"]; ok {
|
||||
delete(norm, "DISCOVERY_URL")
|
||||
if _, ok := norm["DISCOVERY"]; !ok {
|
||||
norm["DISCOVERY"] = val
|
||||
}
|
||||
}
|
||||
|
||||
var sorted sort.StringSlice
|
||||
for k, _ := range norm {
|
||||
sorted = append(sorted, k)
|
||||
}
|
||||
sorted.Sort()
|
||||
|
||||
out += "[Service]\n"
|
||||
|
||||
for _, key := range sorted {
|
||||
val := norm[key]
|
||||
out += fmt.Sprintf("Environment=\"ETCD_%s=%s\"\n", key, val)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Units creates a Unit file drop-in for etcd, using any configured
|
||||
// options and adding a default MachineID if unset.
|
||||
func (ee EtcdEnvironment) Units(root string) ([]system.Unit, error) {
|
||||
if len(ee) < 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if _, ok := ee["name"]; !ok {
|
||||
if machineID := system.MachineID(root); machineID != "" {
|
||||
ee["name"] = machineID
|
||||
} else if hostname, err := system.Hostname(); err == nil {
|
||||
ee["name"] = hostname
|
||||
} else {
|
||||
return nil, errors.New("Unable to determine default etcd name")
|
||||
}
|
||||
}
|
||||
|
||||
etcd := system.Unit{
|
||||
Name: "etcd.service",
|
||||
Runtime: true,
|
||||
DropIn: true,
|
||||
Content: ee.String(),
|
||||
}
|
||||
return []system.Unit{etcd}, nil
|
||||
}
|
184
initialize/etcd_test.go
Normal file
184
initialize/etcd_test.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
func TestEtcdEnvironment(t *testing.T) {
|
||||
cfg := make(EtcdEnvironment, 0)
|
||||
cfg["discovery"] = "http://disco.example.com/foobar"
|
||||
cfg["peer-bind-addr"] = "127.0.0.1:7002"
|
||||
|
||||
env := cfg.String()
|
||||
expect := `[Service]
|
||||
Environment="ETCD_DISCOVERY=http://disco.example.com/foobar"
|
||||
Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
||||
`
|
||||
|
||||
if env != expect {
|
||||
t.Errorf("Generated environment:\n%s\nExpected environment:\n%s", env, expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEtcdEnvironmentDiscoveryURLTranslated(t *testing.T) {
|
||||
cfg := make(EtcdEnvironment, 0)
|
||||
cfg["discovery_url"] = "http://disco.example.com/foobar"
|
||||
cfg["peer-bind-addr"] = "127.0.0.1:7002"
|
||||
|
||||
env := cfg.String()
|
||||
expect := `[Service]
|
||||
Environment="ETCD_DISCOVERY=http://disco.example.com/foobar"
|
||||
Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
||||
`
|
||||
|
||||
if env != expect {
|
||||
t.Errorf("Generated environment:\n%s\nExpected environment:\n%s", env, expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEtcdEnvironmentDiscoveryOverridesDiscoveryURL(t *testing.T) {
|
||||
cfg := make(EtcdEnvironment, 0)
|
||||
cfg["discovery_url"] = "ping"
|
||||
cfg["discovery"] = "pong"
|
||||
cfg["peer-bind-addr"] = "127.0.0.1:7002"
|
||||
|
||||
env := cfg.String()
|
||||
expect := `[Service]
|
||||
Environment="ETCD_DISCOVERY=pong"
|
||||
Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
||||
`
|
||||
|
||||
if env != expect {
|
||||
t.Errorf("Generated environment:\n%s\nExpected environment:\n%s", env, expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEtcdEnvironmentWrittenToDisk(t *testing.T) {
|
||||
ee := EtcdEnvironment{
|
||||
"name": "node001",
|
||||
"discovery": "http://disco.example.com/foobar",
|
||||
"peer-bind-addr": "127.0.0.1:7002",
|
||||
}
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create tempdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
sd := system.NewUnitManager(dir)
|
||||
|
||||
uu, err := ee.Units(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Generating etcd unit failed: %v", err)
|
||||
}
|
||||
if len(uu) != 1 {
|
||||
t.Fatalf("Expected 1 unit to be returned, got %d", len(uu))
|
||||
}
|
||||
u := uu[0]
|
||||
|
||||
dst := u.Destination(dir)
|
||||
os.Stderr.WriteString("writing to " + dir + "\n")
|
||||
if err := sd.PlaceUnit(&u, dst); err != nil {
|
||||
t.Fatalf("Writing of EtcdEnvironment failed: %v", err)
|
||||
}
|
||||
|
||||
fullPath := path.Join(dir, "run", "systemd", "system", "etcd.service.d", "20-cloudinit.conf")
|
||||
|
||||
fi, err := os.Stat(fullPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to stat file: %v", err)
|
||||
}
|
||||
|
||||
if fi.Mode() != os.FileMode(0644) {
|
||||
t.Errorf("File has incorrect mode: %v", fi.Mode())
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to read expected file: %v", err)
|
||||
}
|
||||
|
||||
expect := `[Service]
|
||||
Environment="ETCD_DISCOVERY=http://disco.example.com/foobar"
|
||||
Environment="ETCD_NAME=node001"
|
||||
Environment="ETCD_PEER_BIND_ADDR=127.0.0.1:7002"
|
||||
`
|
||||
if string(contents) != expect {
|
||||
t.Fatalf("File has incorrect contents")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEtcdEnvironmentEmptyNoOp(t *testing.T) {
|
||||
ee := EtcdEnvironment{}
|
||||
uu, err := ee.Units("")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(uu) > 0 {
|
||||
t.Fatalf("Generated etcd units unexpectedly: %v")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEtcdEnvironmentWrittenToDiskDefaultToMachineID(t *testing.T) {
|
||||
ee := EtcdEnvironment{"foo": "bar"}
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create tempdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
sd := system.NewUnitManager(dir)
|
||||
|
||||
os.Mkdir(path.Join(dir, "etc"), os.FileMode(0755))
|
||||
err = ioutil.WriteFile(path.Join(dir, "etc", "machine-id"), []byte("node007"), os.FileMode(0444))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed writing out /etc/machine-id: %v", err)
|
||||
}
|
||||
|
||||
uu, err := ee.Units(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Generating etcd unit failed: %v", err)
|
||||
}
|
||||
if len(uu) == 0 {
|
||||
t.Fatalf("Returned empty etcd units unexpectedly")
|
||||
}
|
||||
u := uu[0]
|
||||
|
||||
dst := u.Destination(dir)
|
||||
os.Stderr.WriteString("writing to " + dir + "\n")
|
||||
if err := sd.PlaceUnit(&u, dst); err != nil {
|
||||
t.Fatalf("Writing of EtcdEnvironment failed: %v", err)
|
||||
}
|
||||
|
||||
fullPath := path.Join(dir, "run", "systemd", "system", "etcd.service.d", "20-cloudinit.conf")
|
||||
|
||||
contents, err := ioutil.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to read expected file: %v", err)
|
||||
}
|
||||
|
||||
expect := `[Service]
|
||||
Environment="ETCD_FOO=bar"
|
||||
Environment="ETCD_NAME=node007"
|
||||
`
|
||||
if string(contents) != expect {
|
||||
t.Fatalf("File has incorrect contents")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEtcdEnvironmentWhenNil(t *testing.T) {
|
||||
// EtcdEnvironment will be a nil map if it wasn't in the yaml
|
||||
var ee EtcdEnvironment
|
||||
if ee != nil {
|
||||
t.Fatalf("EtcdEnvironment is not nil")
|
||||
}
|
||||
uu, err := ee.Units("")
|
||||
if len(uu) != 0 || err != nil {
|
||||
t.Fatalf("Units returned value for nil input")
|
||||
}
|
||||
}
|
35
initialize/fleet.go
Normal file
35
initialize/fleet.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
type FleetEnvironment map[string]string
|
||||
|
||||
func (fe FleetEnvironment) String() (out string) {
|
||||
norm := normalizeSvcEnv(fe)
|
||||
out += "[Service]\n"
|
||||
|
||||
for key, val := range norm {
|
||||
out += fmt.Sprintf("Environment=\"FLEET_%s=%s\"\n", key, val)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Units generates a Unit file drop-in for fleet, if any fleet options were
|
||||
// configured in cloud-config
|
||||
func (fe FleetEnvironment) Units(root string) ([]system.Unit, error) {
|
||||
if len(fe) < 1 {
|
||||
return nil, nil
|
||||
}
|
||||
fleet := system.Unit{
|
||||
Name: "fleet.service",
|
||||
Runtime: true,
|
||||
DropIn: true,
|
||||
Content: fe.String(),
|
||||
}
|
||||
return []system.Unit{fleet}, nil
|
||||
}
|
43
initialize/fleet_test.go
Normal file
43
initialize/fleet_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package initialize
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFleetEnvironment(t *testing.T) {
|
||||
cfg := make(FleetEnvironment, 0)
|
||||
cfg["public-ip"] = "12.34.56.78"
|
||||
|
||||
env := cfg.String()
|
||||
|
||||
expect := `[Service]
|
||||
Environment="FLEET_PUBLIC_IP=12.34.56.78"
|
||||
`
|
||||
|
||||
if env != expect {
|
||||
t.Errorf("Generated environment:\n%s\nExpected environment:\n%s", env, expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFleetUnit(t *testing.T) {
|
||||
cfg := make(FleetEnvironment, 0)
|
||||
uu, err := cfg.Units("/")
|
||||
if len(uu) != 0 {
|
||||
t.Errorf("unexpectedly generated unit with empty FleetEnvironment")
|
||||
}
|
||||
|
||||
cfg["public-ip"] = "12.34.56.78"
|
||||
|
||||
uu, err = cfg.Units("/")
|
||||
if err != nil {
|
||||
t.Errorf("error generating fleet unit: %v", err)
|
||||
}
|
||||
if len(uu) != 1 {
|
||||
t.Fatalf("expected 1 unit generated, got %d", len(uu))
|
||||
}
|
||||
u := uu[0]
|
||||
if !u.Runtime {
|
||||
t.Errorf("bad Runtime for generated fleet unit!")
|
||||
}
|
||||
if !u.DropIn {
|
||||
t.Errorf("bad DropIn for generated fleet unit!")
|
||||
}
|
||||
}
|
32
initialize/github_test.go
Normal file
32
initialize/github_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCloudConfigUsersGithubUser(t *testing.T) {
|
||||
|
||||
contents := `
|
||||
users:
|
||||
- name: elroy
|
||||
coreos-ssh-import-github: bcwaldon
|
||||
`
|
||||
cfg, err := NewCloudConfig(contents)
|
||||
if err != nil {
|
||||
t.Fatalf("Encountered unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Users) != 1 {
|
||||
t.Fatalf("Parsed %d users, expected 1", cfg.Users)
|
||||
}
|
||||
|
||||
user := cfg.Users[0]
|
||||
|
||||
if user.Name != "elroy" {
|
||||
t.Errorf("User name is %q, expected 'elroy'", user.Name)
|
||||
}
|
||||
|
||||
if user.SSHImportGithubUser != "bcwaldon" {
|
||||
t.Errorf("github user is %q, expected 'bcwaldon'", user.SSHImportGithubUser)
|
||||
}
|
||||
}
|
46
initialize/manage_etc_hosts.go
Normal file
46
initialize/manage_etc_hosts.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
const DefaultIpv4Address = "127.0.0.1"
|
||||
|
||||
type EtcHosts string
|
||||
|
||||
func (eh EtcHosts) generateEtcHosts() (out string, err error) {
|
||||
if eh != "localhost" {
|
||||
return "", errors.New("Invalid option to manage_etc_hosts")
|
||||
}
|
||||
|
||||
// use the operating system hostname
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s\n", DefaultIpv4Address, hostname), nil
|
||||
|
||||
}
|
||||
|
||||
func (eh EtcHosts) File(root string) (*system.File, error) {
|
||||
if eh == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
etcHosts, err := eh.generateEtcHosts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &system.File{
|
||||
Path: path.Join("etc", "hosts"),
|
||||
RawFilePermissions: "0644",
|
||||
Content: etcHosts,
|
||||
}, nil
|
||||
}
|
83
initialize/manage_etc_hosts_test.go
Normal file
83
initialize/manage_etc_hosts_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
func TestCloudConfigManageEtcHosts(t *testing.T) {
|
||||
contents := `
|
||||
manage_etc_hosts: localhost
|
||||
`
|
||||
cfg, err := NewCloudConfig(contents)
|
||||
if err != nil {
|
||||
t.Fatalf("Encountered unexpected error: %v", err)
|
||||
}
|
||||
|
||||
manageEtcHosts := cfg.ManageEtcHosts
|
||||
|
||||
if manageEtcHosts != "localhost" {
|
||||
t.Errorf("ManageEtcHosts value is %q, expected 'localhost'", manageEtcHosts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManageEtcHostsInvalidValue(t *testing.T) {
|
||||
eh := EtcHosts("invalid")
|
||||
if f, err := eh.File(""); err == nil || f != nil {
|
||||
t.Fatalf("EtcHosts File succeeded with invalid value!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEtcHostsWrittenToDisk(t *testing.T) {
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create tempdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
eh := EtcHosts("localhost")
|
||||
|
||||
f, err := eh.File(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Error calling File on EtcHosts: %v", err)
|
||||
}
|
||||
if f == nil {
|
||||
t.Fatalf("manageEtcHosts returned nil file unexpectedly")
|
||||
}
|
||||
|
||||
if _, err := system.WriteFile(f, dir); err != nil {
|
||||
t.Fatalf("Error writing EtcHosts: %v", err)
|
||||
}
|
||||
|
||||
fullPath := path.Join(dir, "etc", "hosts")
|
||||
|
||||
fi, err := os.Stat(fullPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to stat file: %v", err)
|
||||
}
|
||||
|
||||
if fi.Mode() != os.FileMode(0644) {
|
||||
t.Errorf("File has incorrect mode: %v", fi.Mode())
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to read expected file: %v", err)
|
||||
}
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to read OS hostname: %v", err)
|
||||
}
|
||||
|
||||
expect := fmt.Sprintf("%s %s\n", DefaultIpv4Address, hostname)
|
||||
|
||||
if string(contents) != expect {
|
||||
t.Fatalf("File has incorrect contents")
|
||||
}
|
||||
}
|
@@ -3,13 +3,11 @@ package initialize
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
)
|
||||
|
||||
// ParseMetaData parses a JSON blob in the OpenStack metadata service format,
|
||||
// and converts it to a partially hydrated CloudConfig.
|
||||
func ParseMetaData(contents string) (*config.CloudConfig, error) {
|
||||
func ParseMetaData(contents string) (*CloudConfig, error) {
|
||||
if len(contents) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -24,7 +22,7 @@ func ParseMetaData(contents string) (*config.CloudConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cfg config.CloudConfig
|
||||
var cfg CloudConfig
|
||||
if len(metadata.SSHAuthorizedKeyMap) > 0 {
|
||||
cfg.SSHAuthorizedKeys = make([]string, 0, len(metadata.SSHAuthorizedKeyMap))
|
||||
for _, name := range sortedKeys(metadata.SSHAuthorizedKeyMap) {
|
||||
|
@@ -1,25 +1,21 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
)
|
||||
import "reflect"
|
||||
import "testing"
|
||||
|
||||
func TestParseMetadata(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
in string
|
||||
want *config.CloudConfig
|
||||
want *CloudConfig
|
||||
err bool
|
||||
}{
|
||||
{"", nil, false},
|
||||
{`garbage, invalid json`, nil, true},
|
||||
{`{"foo": "bar"}`, &config.CloudConfig{}, false},
|
||||
{`{"network_config": {"content_path": "asdf"}}`, &config.CloudConfig{NetworkConfigPath: "asdf"}, false},
|
||||
{`{"hostname": "turkleton"}`, &config.CloudConfig{Hostname: "turkleton"}, false},
|
||||
{`{"public_keys": {"jack": "jill", "bob": "alice"}}`, &config.CloudConfig{SSHAuthorizedKeys: []string{"alice", "jill"}}, false},
|
||||
{`{"unknown": "thing", "hostname": "my_host", "public_keys": {"do": "re", "mi": "fa"}, "network_config": {"content_path": "/root", "blah": "zzz"}}`, &config.CloudConfig{SSHAuthorizedKeys: []string{"re", "fa"}, Hostname: "my_host", NetworkConfigPath: "/root"}, false},
|
||||
{`{"foo": "bar"}`, &CloudConfig{}, false},
|
||||
{`{"network_config": {"content_path": "asdf"}}`, &CloudConfig{NetworkConfigPath: "asdf"}, false},
|
||||
{`{"hostname": "turkleton"}`, &CloudConfig{Hostname: "turkleton"}, false},
|
||||
{`{"public_keys": {"jack": "jill", "bob": "alice"}}`, &CloudConfig{SSHAuthorizedKeys: []string{"alice", "jill"}}, false},
|
||||
{`{"unknown": "thing", "hostname": "my_host", "public_keys": {"do": "re", "mi": "fa"}, "network_config": {"content_path": "/root", "blah": "zzz"}}`, &CloudConfig{SSHAuthorizedKeys: []string{"re", "fa"}, Hostname: "my_host", NetworkConfigPath: "/root"}, false},
|
||||
} {
|
||||
got, err := ParseMetaData(tt.in)
|
||||
if tt.err != (err != nil) {
|
||||
|
41
initialize/oem.go
Normal file
41
initialize/oem.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
type OEMRelease struct {
|
||||
ID string `yaml:"id"`
|
||||
Name string `yaml:"name"`
|
||||
VersionID string `yaml:"version-id"`
|
||||
HomeURL string `yaml:"home-url"`
|
||||
BugReportURL string `yaml:"bug-report-url"`
|
||||
}
|
||||
|
||||
func (oem OEMRelease) String() string {
|
||||
fields := []string{
|
||||
fmt.Sprintf("ID=%s", oem.ID),
|
||||
fmt.Sprintf("VERSION_ID=%s", oem.VersionID),
|
||||
fmt.Sprintf("NAME=%q", oem.Name),
|
||||
fmt.Sprintf("HOME_URL=%q", oem.HomeURL),
|
||||
fmt.Sprintf("BUG_REPORT_URL=%q", oem.BugReportURL),
|
||||
}
|
||||
|
||||
return strings.Join(fields, "\n") + "\n"
|
||||
}
|
||||
|
||||
func (oem OEMRelease) File(root string) (*system.File, error) {
|
||||
if oem.ID == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &system.File{
|
||||
Path: path.Join("etc", "oem-release"),
|
||||
RawFilePermissions: "0644",
|
||||
Content: oem.String(),
|
||||
}, nil
|
||||
}
|
63
initialize/oem_test.go
Normal file
63
initialize/oem_test.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
func TestOEMReleaseWrittenToDisk(t *testing.T) {
|
||||
oem := OEMRelease{
|
||||
ID: "rackspace",
|
||||
Name: "Rackspace Cloud Servers",
|
||||
VersionID: "168.0.0",
|
||||
HomeURL: "https://www.rackspace.com/cloud/servers/",
|
||||
BugReportURL: "https://github.com/coreos/coreos-overlay",
|
||||
}
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create tempdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
f, err := oem.File(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Processing of OEMRelease failed: %v", err)
|
||||
}
|
||||
if f == nil {
|
||||
t.Fatalf("OEMRelease returned nil file unexpectedly")
|
||||
}
|
||||
|
||||
if _, err := system.WriteFile(f, dir); err != nil {
|
||||
t.Fatalf("Writing of OEMRelease failed: %v", err)
|
||||
}
|
||||
|
||||
fullPath := path.Join(dir, "etc", "oem-release")
|
||||
|
||||
fi, err := os.Stat(fullPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to stat file: %v", err)
|
||||
}
|
||||
|
||||
if fi.Mode() != os.FileMode(0644) {
|
||||
t.Errorf("File has incorrect mode: %v", fi.Mode())
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to read expected file: %v", err)
|
||||
}
|
||||
|
||||
expect := `ID=rackspace
|
||||
VERSION_ID=168.0.0
|
||||
NAME="Rackspace Cloud Servers"
|
||||
HOME_URL="https://www.rackspace.com/cloud/servers/"
|
||||
BUG_REPORT_URL="https://github.com/coreos/coreos-overlay"
|
||||
`
|
||||
if string(contents) != expect {
|
||||
t.Fatalf("File has incorrect contents")
|
||||
}
|
||||
}
|
@@ -39,4 +39,31 @@ func TestCloudConfigUsersUrlMarshal(t *testing.T) {
|
||||
if keys[2] != expected {
|
||||
t.Fatalf("expected %s, got %s", expected, keys[2])
|
||||
}
|
||||
|
||||
}
|
||||
func TestCloudConfigUsersSSHImportURL(t *testing.T) {
|
||||
|
||||
contents := `
|
||||
users:
|
||||
- name: elroy
|
||||
coreos-ssh-import-url: https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys
|
||||
`
|
||||
cfg, err := NewCloudConfig(contents)
|
||||
if err != nil {
|
||||
t.Fatalf("Encountered unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Users) != 1 {
|
||||
t.Fatalf("Parsed %d users, expected 1", cfg.Users)
|
||||
}
|
||||
|
||||
user := cfg.Users[0]
|
||||
|
||||
if user.Name != "elroy" {
|
||||
t.Errorf("User name is %q, expected 'elroy'", user.Name)
|
||||
}
|
||||
|
||||
if user.SSHImportURL != "https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys" {
|
||||
t.Errorf("ssh import url is %q, expected 'https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys'", user.SSHImportURL)
|
||||
}
|
||||
}
|
||||
|
165
initialize/update.go
Normal file
165
initialize/update.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
const (
|
||||
locksmithUnit = "locksmithd.service"
|
||||
updateEngineUnit = "update-engine.service"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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=",
|
||||
},
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var out string
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
etcUpdate := path.Join(root, "etc", "coreos", "update.conf")
|
||||
usrUpdate := path.Join(root, "usr", "share", "coreos", "update.conf")
|
||||
|
||||
conf, err := os.Open(etcUpdate)
|
||||
if os.IsNotExist(err) {
|
||||
conf, err = os.Open(usrUpdate)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(conf)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
for _, s := range subs {
|
||||
if strings.HasPrefix(line, s.prefix) {
|
||||
line = s.value
|
||||
s.seen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
out += line
|
||||
out += "\n"
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, s := range subs {
|
||||
if !s.seen {
|
||||
out += s.value
|
||||
out += "\n"
|
||||
}
|
||||
}
|
||||
|
||||
return &system.File{
|
||||
Path: path.Join("etc", "coreos", "update.conf"),
|
||||
RawFilePermissions: "0644",
|
||||
Content: out,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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,
|
||||
Runtime: true,
|
||||
}
|
||||
|
||||
if strategy == "off" {
|
||||
ls.Command = "stop"
|
||||
ls.Mask = true
|
||||
}
|
||||
units = append(units, *ls)
|
||||
}
|
||||
|
||||
rue := false
|
||||
if _, ok := uc["group"]; ok {
|
||||
rue = true
|
||||
}
|
||||
if _, ok := uc["server"]; ok {
|
||||
rue = true
|
||||
}
|
||||
if rue {
|
||||
ue := system.Unit{
|
||||
Name: updateEngineUnit,
|
||||
Command: "restart",
|
||||
}
|
||||
units = append(units, ue)
|
||||
}
|
||||
|
||||
return units, nil
|
||||
}
|
232
initialize/update_test.go
Normal file
232
initialize/update_test.go
Normal file
@@ -0,0 +1,232 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
const (
|
||||
base = `SERVER=https://example.com
|
||||
GROUP=thegroupc`
|
||||
configured = base + `
|
||||
REBOOT_STRATEGY=awesome
|
||||
`
|
||||
expected = base + `
|
||||
REBOOT_STRATEGY=etcd-lock
|
||||
`
|
||||
)
|
||||
|
||||
func setupFixtures(dir string) {
|
||||
os.MkdirAll(path.Join(dir, "usr", "share", "coreos"), 0755)
|
||||
os.MkdirAll(path.Join(dir, "run", "systemd", "system"), 0755)
|
||||
|
||||
ioutil.WriteFile(path.Join(dir, "usr", "share", "coreos", "update.conf"), []byte(base), 0644)
|
||||
}
|
||||
|
||||
func TestEmptyUpdateConfig(t *testing.T) {
|
||||
uc := &UpdateConfig{}
|
||||
f, err := uc.File("")
|
||||
if err != nil {
|
||||
t.Error("unexpected error getting file from empty UpdateConfig")
|
||||
}
|
||||
if f != nil {
|
||||
t.Errorf("getting file from empty UpdateConfig should have returned nil, got %v", f)
|
||||
}
|
||||
uu, err := uc.Units("")
|
||||
if err != nil {
|
||||
t.Error("unexpected error getting unit from empty UpdateConfig")
|
||||
}
|
||||
if len(uu) != 0 {
|
||||
t.Errorf("getting unit from empty UpdateConfig should have returned zero units, got %d", len(uu))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidUpdateOptions(t *testing.T) {
|
||||
uon := &updateOption{
|
||||
key: "numbers",
|
||||
prefix: "numero_",
|
||||
valid: []string{"one", "two"},
|
||||
}
|
||||
uoa := &updateOption{
|
||||
key: "any_will_do",
|
||||
prefix: "any_",
|
||||
}
|
||||
|
||||
if !uon.isValid("one") {
|
||||
t.Error("update option did not accept valid option \"one\"")
|
||||
}
|
||||
if uon.isValid("three") {
|
||||
t.Error("update option accepted invalid option \"three\"")
|
||||
}
|
||||
for _, s := range []string{"one", "asdf", "foobarbaz"} {
|
||||
if !uoa.isValid(s) {
|
||||
t.Errorf("update option with no \"valid\" field did not accept %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
uc := &UpdateConfig{"reboot-strategy": "wizzlewazzle"}
|
||||
f, err := uc.File("")
|
||||
if err == nil {
|
||||
t.Errorf("File did not give an error on invalid UpdateOption")
|
||||
}
|
||||
if f != nil {
|
||||
t.Errorf("File did not return a nil file on invalid UpdateOption")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerGroupOptions(t *testing.T) {
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tempdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
setupFixtures(dir)
|
||||
u := &UpdateConfig{"group": "master", "server": "http://foo.com"}
|
||||
|
||||
want := `
|
||||
GROUP=master
|
||||
SERVER=http://foo.com`
|
||||
|
||||
f, err := u.File(dir)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error getting file from UpdateConfig: %v", err)
|
||||
} else if f == nil {
|
||||
t.Error("unexpectedly got empty file from UpdateConfig")
|
||||
} else {
|
||||
out := strings.Split(f.Content, "\n")
|
||||
sort.Strings(out)
|
||||
got := strings.Join(out, "\n")
|
||||
if got != want {
|
||||
t.Errorf("File has incorrect contents, got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
uu, err := u.Units(dir)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error getting units from UpdateConfig: %v", err)
|
||||
} else if len(uu) != 1 {
|
||||
t.Errorf("unexpected number of files returned from UpdateConfig: want 1, got %d", len(uu))
|
||||
} else {
|
||||
unit := uu[0]
|
||||
if unit.Name != "update-engine.service" {
|
||||
t.Errorf("bad name for generated unit: want update-engine.service, got %s", unit.Name)
|
||||
}
|
||||
if unit.Command != "restart" {
|
||||
t.Errorf("bad command for generated unit: want restart, got %s", unit.Command)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRebootStrategies(t *testing.T) {
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create tempdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
setupFixtures(dir)
|
||||
strategies := []struct {
|
||||
name string
|
||||
line string
|
||||
uMask bool
|
||||
uCommand string
|
||||
}{
|
||||
{"best-effort", "REBOOT_STRATEGY=best-effort", false, "restart"},
|
||||
{"etcd-lock", "REBOOT_STRATEGY=etcd-lock", false, "restart"},
|
||||
{"reboot", "REBOOT_STRATEGY=reboot", false, "restart"},
|
||||
{"off", "REBOOT_STRATEGY=off", true, "stop"},
|
||||
}
|
||||
for _, s := range strategies {
|
||||
uc := &UpdateConfig{"reboot-strategy": s.name}
|
||||
f, err := uc.File(dir)
|
||||
if err != nil {
|
||||
t.Errorf("update failed to generate file for reboot-strategy=%v: %v", s.name, err)
|
||||
} else if f == nil {
|
||||
t.Errorf("generated empty file for reboot-strategy=%v", s.name)
|
||||
} else {
|
||||
seen := false
|
||||
for _, line := range strings.Split(f.Content, "\n") {
|
||||
if line == s.line {
|
||||
seen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !seen {
|
||||
t.Errorf("couldn't find expected line %v for reboot-strategy=%v", s.line)
|
||||
}
|
||||
}
|
||||
uu, err := uc.Units(dir)
|
||||
if err != nil {
|
||||
t.Errorf("failed to generate unit for reboot-strategy=%v!", s.name)
|
||||
} else if len(uu) != 1 {
|
||||
t.Errorf("unexpected number of units for reboot-strategy=%v: %d", s.name, len(uu))
|
||||
} else {
|
||||
u := uu[0]
|
||||
if u.Name != locksmithUnit {
|
||||
t.Errorf("unit generated for reboot strategy=%v had bad name: %v", s.name, u.Name)
|
||||
}
|
||||
if u.Mask != s.uMask {
|
||||
t.Errorf("unit generated for reboot strategy=%v had bad mask: %t", s.name, u.Mask)
|
||||
}
|
||||
if u.Command != s.uCommand {
|
||||
t.Errorf("unit generated for reboot strategy=%v had bad command: %v", s.name, u.Command)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestUpdateConfWrittenToDisk(t *testing.T) {
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create tempdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
setupFixtures(dir)
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
if i == 1 {
|
||||
err = ioutil.WriteFile(path.Join(dir, "etc", "coreos", "update.conf"), []byte(configured), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
uc := &UpdateConfig{"reboot-strategy": "etcd-lock"}
|
||||
|
||||
f, err := uc.File(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Processing UpdateConfig failed: %v", err)
|
||||
} else if f == nil {
|
||||
t.Fatal("Unexpectedly got nil updateconfig file")
|
||||
}
|
||||
|
||||
if _, err := system.WriteFile(f, dir); err != nil {
|
||||
t.Fatalf("Error writing update config: %v", err)
|
||||
}
|
||||
|
||||
fullPath := path.Join(dir, "etc", "coreos", "update.conf")
|
||||
|
||||
fi, err := os.Stat(fullPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to stat file: %v", err)
|
||||
}
|
||||
|
||||
if fi.Mode() != os.FileMode(0644) {
|
||||
t.Errorf("File has incorrect mode: %v", fi.Mode())
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to read expected file: %v", err)
|
||||
}
|
||||
|
||||
if string(contents) != expected {
|
||||
t.Fatalf("File has incorrect contents, got %v, wanted %v", string(contents), expected)
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,7 +5,6 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
@@ -25,7 +24,7 @@ func ParseUserData(contents string) (interface{}, error) {
|
||||
return system.Script(contents), nil
|
||||
} else if header == "#cloud-config" {
|
||||
log.Printf("Parsing user-data as cloud-config")
|
||||
return config.NewCloudConfig(contents)
|
||||
return NewCloudConfig(contents)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Unrecognized user-data header: %s", header)
|
||||
}
|
||||
|
@@ -2,8 +2,6 @@ package initialize
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
)
|
||||
|
||||
func TestParseHeaderCRLF(t *testing.T) {
|
||||
@@ -39,7 +37,7 @@ func TestParseConfigCRLF(t *testing.T) {
|
||||
t.Fatalf("Failed parsing config: %v", err)
|
||||
}
|
||||
|
||||
cfg := ud.(*config.CloudConfig)
|
||||
cfg := ud.(*CloudConfig)
|
||||
|
||||
if cfg.Hostname != "foo" {
|
||||
t.Error("Failed parsing hostname from config")
|
||||
|
@@ -5,7 +5,6 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
"github.com/coreos/coreos-cloudinit/system"
|
||||
)
|
||||
|
||||
@@ -32,21 +31,21 @@ func PersistScriptInWorkspace(script system.Script, workspace string) (string, e
|
||||
|
||||
relpath := strings.TrimPrefix(tmp.Name(), workspace)
|
||||
|
||||
file := system.File{config.File{
|
||||
file := system.File{
|
||||
Path: relpath,
|
||||
RawFilePermissions: "0744",
|
||||
Content: string(script),
|
||||
}}
|
||||
}
|
||||
|
||||
return system.WriteFile(&file, workspace)
|
||||
}
|
||||
|
||||
func PersistUnitNameInWorkspace(name string, workspace string) error {
|
||||
file := system.File{config.File{
|
||||
file := system.File{
|
||||
Path: path.Join("scripts", "unit-name"),
|
||||
RawFilePermissions: "0644",
|
||||
Content: name,
|
||||
}}
|
||||
}
|
||||
_, err := system.WriteFile(&file, workspace)
|
||||
return err
|
||||
}
|
||||
|
Reference in New Issue
Block a user