Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f3f3af79fd | ||
|
0e63aa0f6b | ||
|
b254e17e89 | ||
|
5c059b66f0 | ||
|
c628bef666 | ||
|
2270db3f7a | ||
|
d0d467813d | ||
|
123f111efe | ||
|
44fdf95d99 | ||
|
0a62614eec | ||
|
ea95920f31 | ||
|
b6062f0644 | ||
|
c5fada6e69 |
@@ -1,6 +1,8 @@
|
||||
# Using Cloud-Config
|
||||
|
||||
CoreOS allows you to declaratively customize various OS-level items, such as network configuration, user accounts, and systemd units. This document describes the full list of items we can configure. The `coreos-cloudinit` program uses these files as it configures the OS after startup or during runtime. Your cloud-config is processed during each boot.
|
||||
CoreOS allows you to declaratively customize various OS-level items, such as network configuration, user accounts, and systemd units. This document describes the full list of items we can configure. The `coreos-cloudinit` program uses these files as it configures the OS after startup or during runtime.
|
||||
|
||||
Your cloud-config is processed during each boot. Invalid cloud-config won't be processed but will be logged in the journal. You can validate your cloud-config with the [CoreOS validator]({{site.url}}/validate) or by running `coreos-cloudinit -validate`.
|
||||
|
||||
## Configuration File
|
||||
|
||||
|
@@ -40,7 +40,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
datasourceInterval = 100 * time.Millisecond
|
||||
datasourceMaxInterval = 30 * time.Second
|
||||
datasourceTimeout = 5 * time.Minute
|
||||
|
@@ -17,8 +17,12 @@
|
||||
package cloudsigma
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@@ -51,7 +55,8 @@ func (_ *serverContextService) IsAvailable() bool {
|
||||
}
|
||||
productName := make([]byte, 10)
|
||||
_, err = productNameFile.Read(productName)
|
||||
return err == nil && string(productName) == "CloudSigma"
|
||||
|
||||
return err == nil && string(productName) == "CloudSigma" && hasDHCPLeases()
|
||||
}
|
||||
|
||||
func (_ *serverContextService) AvailabilityChanges() bool {
|
||||
@@ -73,12 +78,16 @@ func (scs *serverContextService) FetchMetadata() ([]byte, error) {
|
||||
UUID string `json:"uuid"`
|
||||
Meta map[string]string `json:"meta"`
|
||||
Nics []struct {
|
||||
Runtime struct {
|
||||
Mac string `json:"mac"`
|
||||
IPv4Conf struct {
|
||||
InterfaceType string `json:"interface_type"`
|
||||
IPv4 struct {
|
||||
IP string `json:"uuid"`
|
||||
} `json:"ip_v4"`
|
||||
} `json:"runtime"`
|
||||
IP struct {
|
||||
UUID string `json:"uuid"`
|
||||
} `json:"ip"`
|
||||
} `json:"ip_v4_conf"`
|
||||
VLAN struct {
|
||||
UUID string `json:"uuid"`
|
||||
} `json:"vlan"`
|
||||
} `json:"nics"`
|
||||
}
|
||||
outputMetadata struct {
|
||||
@@ -112,11 +121,12 @@ func (scs *serverContextService) FetchMetadata() ([]byte, error) {
|
||||
}
|
||||
|
||||
for _, nic := range inputMetadata.Nics {
|
||||
if nic.Runtime.IPv4.IP != "" {
|
||||
if nic.Runtime.InterfaceType == "public" {
|
||||
outputMetadata.PublicIPv4 = nic.Runtime.IPv4.IP
|
||||
} else {
|
||||
outputMetadata.LocalIPv4 = nic.Runtime.IPv4.IP
|
||||
if nic.IPv4Conf.IP.UUID != "" {
|
||||
outputMetadata.PublicIPv4 = nic.IPv4Conf.IP.UUID
|
||||
}
|
||||
if nic.VLAN.UUID != "" {
|
||||
if localIP, err := scs.findLocalIP(nic.Mac); err == nil {
|
||||
outputMetadata.LocalIPv4 = localIP
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,6 +156,36 @@ func (scs *serverContextService) FetchNetworkConfig(a string) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (scs *serverContextService) findLocalIP(mac string) (string, error) {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ifaceMac, err := net.ParseMAC(mac)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
if !bytes.Equal(iface.HardwareAddr, ifaceMac) {
|
||||
continue
|
||||
}
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
switch ip := addr.(type) {
|
||||
case *net.IPNet:
|
||||
if ip.IP.To4() != nil {
|
||||
return ip.IP.To4().String(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("Local IP not found")
|
||||
}
|
||||
|
||||
func isBase64Encoded(field string, userdata map[string]string) bool {
|
||||
base64Fields, ok := userdata["base64_fields"]
|
||||
if !ok {
|
||||
@@ -159,3 +199,8 @@ func isBase64Encoded(field string, userdata map[string]string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasDHCPLeases() bool {
|
||||
files, err := ioutil.ReadDir("/run/systemd/netif/leases/")
|
||||
return err == nil && len(files) > 0
|
||||
}
|
||||
|
@@ -74,14 +74,41 @@ func TestServerContextFetchMetadata(t *testing.T) {
|
||||
"name": "coreos",
|
||||
"nics": [
|
||||
{
|
||||
"runtime": {
|
||||
"interface_type": "public",
|
||||
"ip_v4": {
|
||||
"boot_order": null,
|
||||
"ip_v4_conf": {
|
||||
"conf": "dhcp",
|
||||
"ip": {
|
||||
"gateway": "31.171.244.1",
|
||||
"meta": {},
|
||||
"nameservers": [
|
||||
"178.22.66.167",
|
||||
"178.22.71.56",
|
||||
"8.8.8.8"
|
||||
],
|
||||
"netmask": 22,
|
||||
"tags": [],
|
||||
"uuid": "31.171.251.74"
|
||||
},
|
||||
"ip_v6": null
|
||||
}
|
||||
},
|
||||
"ip_v6_conf": null,
|
||||
"mac": "22:3d:09:6b:90:f3",
|
||||
"model": "virtio",
|
||||
"vlan": null
|
||||
},
|
||||
{
|
||||
"boot_order": null,
|
||||
"ip_v4_conf": null,
|
||||
"ip_v6_conf": null,
|
||||
"mac": "22:ae:4a:fb:8f:31",
|
||||
"model": "virtio",
|
||||
"vlan": {
|
||||
"meta": {
|
||||
"description": "",
|
||||
"name": "CoreOS"
|
||||
},
|
||||
"tags": [],
|
||||
"uuid": "5dec030e-25b8-4621-a5a4-a3302c9d9619"
|
||||
}
|
||||
}
|
||||
],
|
||||
"smp": 2,
|
||||
@@ -106,12 +133,8 @@ func TestServerContextFetchMetadata(t *testing.T) {
|
||||
t.Error("Public SSH Keys are not being read properly")
|
||||
}
|
||||
|
||||
if metadata.LocalIPv4 != "" {
|
||||
t.Errorf("Local IP is not empty but %s instead", metadata.LocalIPv4)
|
||||
}
|
||||
|
||||
if metadata.PublicIPv4 != "31.171.251.74" {
|
||||
t.Errorf("Local IP is not 31.171.251.74 but %s instead", metadata.PublicIPv4)
|
||||
t.Errorf("Public IP is not 31.171.251.74 but %s instead", metadata.PublicIPv4)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -200,6 +200,7 @@ func processUnits(units []system.Unit, root string, um system.UnitManager) error
|
||||
}
|
||||
actions := make([]action, 0, len(units))
|
||||
reload := false
|
||||
restartNetworkd := false
|
||||
for _, unit := range units {
|
||||
if unit.Name == "" {
|
||||
log.Printf("Skipping unit without name")
|
||||
@@ -251,8 +252,7 @@ func processUnits(units []system.Unit, root string, um system.UnitManager) error
|
||||
}
|
||||
|
||||
if unit.Group() == "network" {
|
||||
networkd := system.Unit{Unit: config.Unit{Name: "systemd-networkd.service"}}
|
||||
actions = append(actions, action{networkd, "restart"})
|
||||
restartNetworkd = true
|
||||
} else if unit.Command != "" {
|
||||
actions = append(actions, action{unit, unit.Command})
|
||||
}
|
||||
@@ -264,13 +264,23 @@ func processUnits(units []system.Unit, root string, um system.UnitManager) error
|
||||
}
|
||||
}
|
||||
|
||||
if restartNetworkd {
|
||||
log.Printf("Restarting systemd-networkd")
|
||||
networkd := system.Unit{Unit: config.Unit{Name: "systemd-networkd.service"}}
|
||||
res, err := um.RunUnitCommand(networkd, "restart")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Restarted systemd-networkd (%s)", res)
|
||||
}
|
||||
|
||||
for _, action := range actions {
|
||||
log.Printf("Calling unit command %q on %q'", action.command, action.unit.Name)
|
||||
res, err := um.RunUnitCommand(action.unit, action.command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Result of %q on %q': %s", action.command, action.unit.Name, res)
|
||||
log.Printf("Result of %q on %q: %s", action.command, action.unit.Name, res)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -29,10 +29,15 @@ type TestUnitManager struct {
|
||||
enabled []string
|
||||
masked []string
|
||||
unmasked []string
|
||||
commands map[string]string
|
||||
commands []UnitAction
|
||||
reload bool
|
||||
}
|
||||
|
||||
type UnitAction struct {
|
||||
unit string
|
||||
command string
|
||||
}
|
||||
|
||||
func (tum *TestUnitManager) PlaceUnit(u system.Unit) error {
|
||||
tum.placed = append(tum.placed, u.Name)
|
||||
return nil
|
||||
@@ -46,8 +51,7 @@ func (tum *TestUnitManager) EnableUnitFile(u system.Unit) error {
|
||||
return nil
|
||||
}
|
||||
func (tum *TestUnitManager) RunUnitCommand(u system.Unit, c string) (string, error) {
|
||||
tum.commands = make(map[string]string)
|
||||
tum.commands[u.Name] = c
|
||||
tum.commands = append(tum.commands, UnitAction{u.Name, c})
|
||||
return "", nil
|
||||
}
|
||||
func (tum *TestUnitManager) DaemonReload() error {
|
||||
@@ -83,13 +87,26 @@ func TestProcessUnits(t *testing.T) {
|
||||
{
|
||||
units: []system.Unit{
|
||||
system.Unit{Unit: config.Unit{
|
||||
Name: "bar.network",
|
||||
Name: "baz.service",
|
||||
Content: "[Service]\nExecStart=/bin/baz",
|
||||
Command: "start",
|
||||
}},
|
||||
system.Unit{Unit: config.Unit{
|
||||
Name: "foo.network",
|
||||
Content: "[Network]\nFoo=true",
|
||||
}},
|
||||
system.Unit{Unit: config.Unit{
|
||||
Name: "bar.network",
|
||||
Content: "[Network]\nBar=true",
|
||||
}},
|
||||
},
|
||||
result: TestUnitManager{
|
||||
commands: map[string]string{
|
||||
"systemd-networkd.service": "restart",
|
||||
placed: []string{"baz.service", "foo.network", "bar.network"},
|
||||
commands: []UnitAction{
|
||||
UnitAction{"systemd-networkd.service", "restart"},
|
||||
UnitAction{"baz.service", "start"},
|
||||
},
|
||||
reload: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
Reference in New Issue
Block a user