2014-03-05 04:36:05 +04:00
package main
import (
2014-05-23 02:00:41 +04:00
"encoding/json"
2014-03-05 04:36:05 +04:00
"flag"
2014-03-18 20:00:41 +04:00
"fmt"
2014-05-23 02:00:41 +04:00
"io/ioutil"
2014-03-18 20:00:41 +04:00
"os"
2014-05-23 02:00:41 +04:00
"path"
2014-03-05 04:36:05 +04:00
2014-03-18 20:00:41 +04:00
"github.com/coreos/coreos-cloudinit/datasource"
"github.com/coreos/coreos-cloudinit/initialize"
2014-05-23 02:00:41 +04:00
"github.com/coreos/coreos-cloudinit/network"
2014-03-18 20:00:41 +04:00
"github.com/coreos/coreos-cloudinit/system"
2014-03-05 04:36:05 +04:00
)
2014-06-06 22:10:48 +04:00
const version = "0.7.5"
2014-03-05 05:01:58 +04:00
2014-03-05 04:36:05 +04:00
func main ( ) {
2014-03-05 05:01:58 +04:00
var printVersion bool
flag . BoolVar ( & printVersion , "version" , false , "Print the version and exit" )
2014-03-19 07:47:20 +04:00
var ignoreFailure bool
flag . BoolVar ( & ignoreFailure , "ignore-failure" , false , "Exits with 0 status in the event of malformed input from user-data" )
2014-03-05 04:36:05 +04:00
var file string
2014-03-05 05:06:52 +04:00
flag . StringVar ( & file , "from-file" , "" , "Read user-data from provided file" )
2014-05-23 00:48:26 +04:00
var configdrive string
flag . StringVar ( & configdrive , "from-configdrive" , "" , "Read user-data from provided cloud-drive directory" )
2014-03-05 05:06:52 +04:00
var url string
flag . StringVar ( & url , "from-url" , "" , "Download user-data from provided url" )
2014-03-05 04:36:05 +04:00
2014-04-23 02:36:07 +04:00
var useProcCmdline bool
flag . BoolVar ( & useProcCmdline , "from-proc-cmdline" , false , fmt . Sprintf ( "Parse %s for '%s=<url>', using the cloud-config served by an HTTP GET to <url>" , datasource . ProcCmdlineLocation , datasource . ProcCmdlineCloudConfigFlag ) )
2014-05-23 02:00:41 +04:00
var convertNetconf string
flag . StringVar ( & convertNetconf , "convert-netconf" , "" , "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files (requires the -from-configdrive flag)" )
2014-03-05 04:36:05 +04:00
var workspace string
flag . StringVar ( & workspace , "workspace" , "/var/lib/coreos-cloudinit" , "Base directory coreos-cloudinit should use to store data" )
2014-03-06 02:30:38 +04:00
var sshKeyName string
2014-03-18 20:00:41 +04:00
flag . StringVar ( & sshKeyName , "ssh-key-name" , initialize . DefaultSSHKeyName , "Add SSH keys to the system with the given name" )
2014-03-06 02:30:38 +04:00
2014-03-05 04:36:05 +04:00
flag . Parse ( )
2014-03-05 05:01:58 +04:00
if printVersion == true {
fmt . Printf ( "coreos-cloudinit version %s\n" , version )
os . Exit ( 0 )
}
2014-03-18 20:00:41 +04:00
var ds datasource . Datasource
2014-03-05 04:36:05 +04:00
if file != "" {
2014-03-18 20:00:41 +04:00
ds = datasource . NewLocalFile ( file )
2014-03-05 05:06:52 +04:00
} else if url != "" {
2014-03-18 20:00:41 +04:00
ds = datasource . NewMetadataService ( url )
2014-05-23 00:48:26 +04:00
} else if configdrive != "" {
ds = datasource . NewConfigDrive ( configdrive )
2014-04-23 02:36:07 +04:00
} else if useProcCmdline {
ds = datasource . NewProcCmdline ( )
2014-03-05 05:06:52 +04:00
} else {
2014-05-23 00:48:26 +04:00
fmt . Println ( "Provide one of --from-file, --from-configdrive, --from-url or --from-proc-cmdline" )
2014-03-05 05:06:52 +04:00
os . Exit ( 1 )
2014-03-05 04:36:05 +04:00
}
2014-05-23 02:00:41 +04:00
if convertNetconf != "" && configdrive == "" {
fmt . Println ( "-convert-netconf flag requires -from-configdrive" )
os . Exit ( 1 )
}
switch convertNetconf {
case "" :
case "debian" :
default :
fmt . Printf ( "Invalid option to -convert-netconf: '%s'. Supported options: 'debian'\n" , convertNetconf )
2014-03-05 05:06:52 +04:00
os . Exit ( 1 )
2014-03-05 04:36:05 +04:00
}
2014-05-23 02:00:41 +04:00
fmt . Printf ( "Fetching user-data from datasource of type %q\n" , ds . Type ( ) )
2014-03-21 21:35:18 +04:00
userdataBytes , err := ds . Fetch ( )
2014-03-18 20:00:41 +04:00
if err != nil {
2014-05-23 02:00:41 +04:00
fmt . Printf ( "Failed fetching user-data from datasource: %v\n" , err )
2014-03-19 07:47:20 +04:00
if ignoreFailure {
os . Exit ( 0 )
} else {
os . Exit ( 1 )
}
2014-03-18 20:00:41 +04:00
}
2014-03-21 21:35:18 +04:00
env := initialize . NewEnvironment ( "/" , workspace )
2014-05-23 01:02:10 +04:00
if len ( userdataBytes ) > 0 {
if err := processUserdata ( string ( userdataBytes ) , env ) ; err != nil {
2014-05-23 02:00:41 +04:00
fmt . Printf ( "Failed resolving user-data: %v\n" , err )
2014-05-23 01:02:10 +04:00
if ! ignoreFailure {
os . Exit ( 1 )
}
}
} else {
2014-05-23 02:00:41 +04:00
fmt . Println ( "No user data to handle." )
2014-03-14 08:12:14 +04:00
}
2014-05-23 02:00:41 +04:00
if convertNetconf != "" {
if err := processNetconf ( convertNetconf , configdrive ) ; err != nil {
fmt . Printf ( "Failed to process network config: %v\n" , err )
if ! ignoreFailure {
os . Exit ( 1 )
}
}
2014-05-23 01:02:10 +04:00
}
}
2014-03-21 21:35:18 +04:00
2014-05-23 01:02:10 +04:00
func processUserdata ( userdata string , env * initialize . Environment ) error {
2014-03-21 21:35:18 +04:00
userdata = env . Apply ( userdata )
2014-04-22 00:31:23 +04:00
parsed , err := initialize . ParseUserData ( userdata )
2014-03-05 04:36:05 +04:00
if err != nil {
2014-05-23 02:00:41 +04:00
fmt . Printf ( "Failed parsing user-data: %v\n" , err )
2014-05-23 01:02:10 +04:00
return err
2014-03-05 04:36:05 +04:00
}
2014-03-18 20:00:41 +04:00
err = initialize . PrepWorkspace ( env . Workspace ( ) )
2014-03-05 04:36:05 +04:00
if err != nil {
2014-05-23 02:00:41 +04:00
fmt . Printf ( "Failed preparing workspace: %v\n" , err )
return err
2014-03-05 04:36:05 +04:00
}
switch t := parsed . ( type ) {
2014-03-18 20:00:41 +04:00
case initialize . CloudConfig :
err = initialize . Apply ( t , env )
case system . Script :
2014-03-05 04:36:05 +04:00
var path string
2014-03-18 20:00:41 +04:00
path , err = initialize . PersistScriptInWorkspace ( t , env . Workspace ( ) )
2014-03-05 04:36:05 +04:00
if err == nil {
2014-03-05 09:02:20 +04:00
var name string
2014-03-18 20:00:41 +04:00
name , err = system . ExecuteScript ( path )
2014-05-23 01:02:10 +04:00
initialize . PersistUnitNameInWorkspace ( name , env . Workspace ( ) )
2014-03-05 04:36:05 +04:00
}
}
2014-05-23 01:02:10 +04:00
return err
2014-03-05 04:36:05 +04:00
}
2014-05-23 02:00:41 +04:00
func processNetconf ( convertNetconf , configdrive string ) error {
openstackRoot := path . Join ( configdrive , "openstack" )
metadataFilename := path . Join ( openstackRoot , "latest" , "meta_data.json" )
metadataBytes , err := ioutil . ReadFile ( metadataFilename )
2014-03-05 04:36:05 +04:00
if err != nil {
2014-05-23 02:00:41 +04:00
return err
}
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 {
return err
}
var interfaces [ ] network . InterfaceGenerator
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
2014-03-05 04:36:05 +04:00
}
2014-05-23 02:00:41 +04:00
return system . RestartNetwork ( interfaces )
2014-03-05 04:36:05 +04:00
}