Merge pull request #152 from crawford/metadata
feat(meta_data): Add partial support for meta_data.json
This commit is contained in:
commit
5c89afc18a
@ -1,16 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/coreos/coreos-cloudinit/datasource"
|
"github.com/coreos/coreos-cloudinit/datasource"
|
||||||
"github.com/coreos/coreos-cloudinit/initialize"
|
"github.com/coreos/coreos-cloudinit/initialize"
|
||||||
"github.com/coreos/coreos-cloudinit/network"
|
|
||||||
"github.com/coreos/coreos-cloudinit/system"
|
"github.com/coreos/coreos-cloudinit/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -79,7 +75,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type())
|
fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type())
|
||||||
userdataBytes, err := ds.Fetch()
|
userdataBytes, err := ds.FetchUserdata()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed fetching user-data from datasource: %v\n", err)
|
fmt.Printf("Failed fetching user-data from datasource: %v\n", err)
|
||||||
if ignoreFailure {
|
if ignoreFailure {
|
||||||
@ -89,25 +85,38 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
env := initialize.NewEnvironment("/", workspace)
|
fmt.Printf("Fetching meta-data from datasource of type %q\n", ds.Type())
|
||||||
|
metadataBytes, err := ds.FetchMetadata()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed fetching meta-data from datasource: %v\n", err)
|
||||||
|
if ignoreFailure {
|
||||||
|
os.Exit(0)
|
||||||
|
} else {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env := initialize.NewEnvironment("/", ds.ConfigRoot(), workspace, convertNetconf, sshKeyName)
|
||||||
if len(userdataBytes) > 0 {
|
if len(userdataBytes) > 0 {
|
||||||
if err := processUserdata(string(userdataBytes), env); err != nil {
|
if err := processUserdata(string(userdataBytes), env); err != nil {
|
||||||
fmt.Printf("Failed resolving user-data: %v\n", err)
|
fmt.Printf("Failed to process user-data: %v\n", err)
|
||||||
if !ignoreFailure {
|
if !ignoreFailure {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("No user data to handle.")
|
fmt.Println("No user-data to handle.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if convertNetconf != "" {
|
if len(metadataBytes) > 0 {
|
||||||
if err := processNetconf(convertNetconf, configdrive); err != nil {
|
if err := processMetadata(string(metadataBytes), env); err != nil {
|
||||||
fmt.Printf("Failed to process network config: %v\n", err)
|
fmt.Printf("Failed to process meta-data: %v\n", err)
|
||||||
if !ignoreFailure {
|
if !ignoreFailure {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("No meta-data to handle.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,47 +151,17 @@ func processUserdata(userdata string, env *initialize.Environment) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func processNetconf(convertNetconf, configdrive string) error {
|
func processMetadata(metadata string, env *initialize.Environment) error {
|
||||||
openstackRoot := path.Join(configdrive, "openstack")
|
parsed, err := initialize.ParseMetaData(metadata)
|
||||||
metadataFilename := path.Join(openstackRoot, "latest", "meta_data.json")
|
|
||||||
metadataBytes, err := ioutil.ReadFile(metadataFilename)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("Failed parsing meta-data: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = initialize.PrepWorkspace(env.Workspace())
|
||||||
var metadata struct {
|
|
||||||
NetworkConfig struct {
|
|
||||||
ContentPath string `json:"content_path"`
|
|
||||||
} `json:"network_config"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
configPath := metadata.NetworkConfig.ContentPath
|
|
||||||
if configPath == "" {
|
|
||||||
fmt.Printf("No network config specified in %q.\n", metadataFilename)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
netconfBytes, err := ioutil.ReadFile(path.Join(openstackRoot, configPath))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("Failed preparing workspace: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var interfaces []network.InterfaceGenerator
|
return initialize.Apply(parsed, env)
|
||||||
switch convertNetconf {
|
|
||||||
case "debian":
|
|
||||||
interfaces, err = network.ProcessDebianNetconf(string(netconfBytes))
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Unsupported network config format %q", convertNetconf)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := system.WriteNetworkdConfigs(interfaces); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return system.RestartNetwork(interfaces)
|
|
||||||
}
|
}
|
||||||
|
@ -7,21 +7,33 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type configDrive struct {
|
type configDrive struct {
|
||||||
path string
|
root string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfigDrive(path string) *configDrive {
|
func NewConfigDrive(root string) *configDrive {
|
||||||
return &configDrive{path}
|
return &configDrive{path.Join(root, "openstack")}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *configDrive) Fetch() ([]byte, error) {
|
func (self *configDrive) ConfigRoot() string {
|
||||||
data, err := ioutil.ReadFile(path.Join(self.path, "openstack", "latest", "user_data"))
|
return self.root
|
||||||
if os.IsNotExist(err) {
|
|
||||||
err = nil
|
|
||||||
}
|
}
|
||||||
return data, err
|
|
||||||
|
func (self *configDrive) FetchMetadata() ([]byte, error) {
|
||||||
|
return self.readFile("meta_data.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *configDrive) FetchUserdata() ([]byte, error) {
|
||||||
|
return self.readFile("user_data")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *configDrive) Type() string {
|
func (self *configDrive) Type() string {
|
||||||
return "cloud-drive"
|
return "cloud-drive"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *configDrive) readFile(filename string) ([]byte, error) {
|
||||||
|
data, err := ioutil.ReadFile(path.Join(self.root, "latest", filename))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package datasource
|
package datasource
|
||||||
|
|
||||||
type Datasource interface {
|
type Datasource interface {
|
||||||
Fetch() ([]byte, error)
|
ConfigRoot() string
|
||||||
|
FetchMetadata() ([]byte, error)
|
||||||
|
FetchUserdata() ([]byte, error)
|
||||||
Type() string
|
Type() string
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,15 @@ func NewLocalFile(path string) *localFile {
|
|||||||
return &localFile{path}
|
return &localFile{path}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *localFile) Fetch() ([]byte, error) {
|
func (self *localFile) ConfigRoot() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *localFile) FetchMetadata() ([]byte, error) {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *localFile) FetchUserdata() ([]byte, error) {
|
||||||
return ioutil.ReadFile(self.path)
|
return ioutil.ReadFile(self.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,15 @@ func NewMetadataService(url string) *metadataService {
|
|||||||
return &metadataService{url}
|
return &metadataService{url}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *metadataService) Fetch() ([]byte, error) {
|
func (self *metadataService) ConfigRoot() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *metadataService) FetchMetadata() ([]byte, error) {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *metadataService) FetchUserdata() ([]byte, error) {
|
||||||
client := pkg.NewHttpClient()
|
client := pkg.NewHttpClient()
|
||||||
return client.Get(ms.url)
|
return client.Get(ms.url)
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,15 @@ func NewProcCmdline() *procCmdline {
|
|||||||
return &procCmdline{Location: ProcCmdlineLocation}
|
return &procCmdline{Location: ProcCmdlineLocation}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *procCmdline) Fetch() ([]byte, error) {
|
func (self *procCmdline) ConfigRoot() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *procCmdline) FetchMetadata() ([]byte, error) {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *procCmdline) FetchUserdata() ([]byte, error) {
|
||||||
contents, err := ioutil.ReadFile(self.Location)
|
contents, err := ioutil.ReadFile(self.Location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -77,7 +77,7 @@ func TestProcCmdlineAndFetchConfig(t *testing.T) {
|
|||||||
|
|
||||||
p := NewProcCmdline()
|
p := NewProcCmdline()
|
||||||
p.Location = file.Name()
|
p.Location = file.Name()
|
||||||
cfg, err := p.Fetch()
|
cfg, err := p.FetchUserdata()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Test produced error: %v", err)
|
t.Errorf("Test produced error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,13 @@ package initialize
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"path"
|
||||||
|
|
||||||
"github.com/coreos/coreos-cloudinit/third_party/launchpad.net/goyaml"
|
"github.com/coreos/coreos-cloudinit/third_party/launchpad.net/goyaml"
|
||||||
|
|
||||||
|
"github.com/coreos/coreos-cloudinit/network"
|
||||||
"github.com/coreos/coreos-cloudinit/system"
|
"github.com/coreos/coreos-cloudinit/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,6 +41,7 @@ type CloudConfig struct {
|
|||||||
Hostname string
|
Hostname string
|
||||||
Users []system.User
|
Users []system.User
|
||||||
ManageEtcHosts EtcHosts `yaml:"manage_etc_hosts"`
|
ManageEtcHosts EtcHosts `yaml:"manage_etc_hosts"`
|
||||||
|
NetworkConfigPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
type warner func(format string, v ...interface{})
|
type warner func(format string, v ...interface{})
|
||||||
@ -227,6 +231,32 @@ func Apply(cfg CloudConfig, env *Environment) error {
|
|||||||
log.Printf("Wrote file %s to filesystem", path)
|
log.Printf("Wrote file %s to filesystem", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if env.NetconfType() != "" {
|
||||||
|
netconfBytes, err := ioutil.ReadFile(path.Join(env.ConfigRoot(), cfg.NetworkConfigPath))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var interfaces []network.InterfaceGenerator
|
||||||
|
switch env.NetconfType() {
|
||||||
|
case "debian":
|
||||||
|
interfaces, err = network.ProcessDebianNetconf(string(netconfBytes))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Unsupported network config format %q", env.NetconfType())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := system.WriteNetworkdConfigs(interfaces); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := system.RestartNetwork(interfaces); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
commands := make(map[string]string, 0)
|
commands := make(map[string]string, 0)
|
||||||
reload := false
|
reload := false
|
||||||
for _, unit := range cfg.Coreos.Units {
|
for _, unit := range cfg.Coreos.Units {
|
||||||
|
@ -10,17 +10,19 @@ const DefaultSSHKeyName = "coreos-cloudinit"
|
|||||||
|
|
||||||
type Environment struct {
|
type Environment struct {
|
||||||
root string
|
root string
|
||||||
|
configRoot string
|
||||||
workspace string
|
workspace string
|
||||||
|
netconfType string
|
||||||
sshKeyName string
|
sshKeyName string
|
||||||
substitutions map[string]string
|
substitutions map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEnvironment(root, workspace string) *Environment {
|
func NewEnvironment(root, configRoot, workspace, netconfType, sshKeyName string) *Environment {
|
||||||
substitutions := map[string]string{
|
substitutions := map[string]string{
|
||||||
"$public_ipv4": os.Getenv("COREOS_PUBLIC_IPV4"),
|
"$public_ipv4": os.Getenv("COREOS_PUBLIC_IPV4"),
|
||||||
"$private_ipv4": os.Getenv("COREOS_PRIVATE_IPV4"),
|
"$private_ipv4": os.Getenv("COREOS_PRIVATE_IPV4"),
|
||||||
}
|
}
|
||||||
return &Environment{root, workspace, DefaultSSHKeyName, substitutions}
|
return &Environment{root, configRoot, workspace, netconfType, sshKeyName, substitutions}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Environment) Workspace() string {
|
func (self *Environment) Workspace() string {
|
||||||
@ -31,6 +33,14 @@ func (self *Environment) Root() string {
|
|||||||
return self.root
|
return self.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Environment) ConfigRoot() string {
|
||||||
|
return self.configRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Environment) NetconfType() string {
|
||||||
|
return self.netconfType
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Environment) SSHKeyName() string {
|
func (self *Environment) SSHKeyName() string {
|
||||||
return self.sshKeyName
|
return self.sshKeyName
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
func TestEnvironmentApply(t *testing.T) {
|
func TestEnvironmentApply(t *testing.T) {
|
||||||
os.Setenv("COREOS_PUBLIC_IPV4", "192.0.2.3")
|
os.Setenv("COREOS_PUBLIC_IPV4", "192.0.2.3")
|
||||||
os.Setenv("COREOS_PRIVATE_IPV4", "192.0.2.203")
|
os.Setenv("COREOS_PRIVATE_IPV4", "192.0.2.203")
|
||||||
env := NewEnvironment("./", "./")
|
env := NewEnvironment("./", "./", "./", "", "")
|
||||||
input := `[Service]
|
input := `[Service]
|
||||||
ExecStart=/usr/bin/echo "$public_ipv4"
|
ExecStart=/usr/bin/echo "$public_ipv4"
|
||||||
ExecStop=/usr/bin/echo $private_ipv4
|
ExecStop=/usr/bin/echo $private_ipv4
|
||||||
|
26
initialize/meta_data.go
Normal file
26
initialize/meta_data.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseMetaData(contents string) (cfg CloudConfig, err error) {
|
||||||
|
var metadata struct {
|
||||||
|
SSHAuthorizedKeyMap map[string]string `json:"public_keys"`
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
NetworkConfig struct {
|
||||||
|
ContentPath string `json:"content_path"`
|
||||||
|
} `json:"network_config"`
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal([]byte(contents), &metadata); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.SSHAuthorizedKeys = make([]string, 0, len(metadata.SSHAuthorizedKeyMap))
|
||||||
|
for _, key := range metadata.SSHAuthorizedKeyMap {
|
||||||
|
cfg.SSHAuthorizedKeys = append(cfg.SSHAuthorizedKeys, key)
|
||||||
|
}
|
||||||
|
cfg.Hostname = metadata.Hostname
|
||||||
|
cfg.NetworkConfigPath = metadata.NetworkConfig.ContentPath
|
||||||
|
return
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user