github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/container/userdata.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package container 5 6 import ( 7 "bytes" 8 "io/ioutil" 9 "path/filepath" 10 "text/template" 11 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 15 coreCloudinit "github.com/juju/juju/cloudinit" 16 "github.com/juju/juju/environs/cloudinit" 17 ) 18 19 var ( 20 logger = loggo.GetLogger("juju.container") 21 ) 22 23 // WriteUserData generates the cloud-init user-data using the 24 // specified machine and network config for a container, and writes 25 // the serialized form out to a cloud-init file in the directory 26 // specified. 27 func WriteUserData( 28 machineConfig *cloudinit.MachineConfig, 29 networkConfig *NetworkConfig, 30 directory string, 31 ) (string, error) { 32 userData, err := cloudInitUserData(machineConfig, networkConfig) 33 if err != nil { 34 logger.Errorf("failed to create user data: %v", err) 35 return "", err 36 } 37 return WriteCloudInitFile(directory, userData) 38 } 39 40 // WriteCloudInitFile writes the data out to a cloud-init file in the 41 // directory specified, and returns the filename. 42 func WriteCloudInitFile(directory string, userData []byte) (string, error) { 43 userDataFilename := filepath.Join(directory, "cloud-init") 44 if err := ioutil.WriteFile(userDataFilename, userData, 0644); err != nil { 45 logger.Errorf("failed to write user data: %v", err) 46 return "", err 47 } 48 return userDataFilename, nil 49 } 50 51 // networkConfigTemplate defines how to render /etc/network/interfaces 52 // file for a container with one or more NICs. 53 const networkConfigTemplate = ` 54 # loopback interface 55 auto lo 56 iface lo inet loopback{{define "static"}} 57 {{.InterfaceName | printf "# interface %q"}}{{if not .NoAutoStart}} 58 auto {{.InterfaceName}}{{end}} 59 iface {{.InterfaceName}} inet static 60 address {{.Address.Value}} 61 netmask 255.255.255.255{{if gt (len .DNSServers) 0}} 62 dns-nameservers{{range $dns := .DNSServers}} {{$dns.Value}}{{end}}{{end}} 63 pre-up ip route add {{.GatewayAddress.Value}} dev {{.InterfaceName}} 64 pre-up ip route add default via {{.GatewayAddress.Value}} 65 post-down ip route del default via {{.GatewayAddress.Value}} 66 post-down ip route del {{.GatewayAddress.Value}} dev {{.InterfaceName}} 67 {{end}}{{define "dhcp"}} 68 {{.InterfaceName | printf "# interface %q"}}{{if not .NoAutoStart}} 69 auto {{.InterfaceName}}{{end}} 70 iface {{.InterfaceName}} inet dhcp 71 {{end}}{{range $nic := . }}{{if eq $nic.ConfigType "static"}} 72 {{template "static" $nic}}{{else}}{{template "dhcp" $nic}}{{end}}{{end}}` 73 74 var networkInterfacesFile = "/etc/network/interfaces" 75 76 // newCloudInitConfigWithNetworks creates a cloud-init config which 77 // might include per-interface networking config if 78 // networkConfig.Interfaces is not empty. 79 func newCloudInitConfigWithNetworks(networkConfig *NetworkConfig) (*coreCloudinit.Config, error) { 80 cloudConfig := coreCloudinit.New() 81 if networkConfig == nil || len(networkConfig.Interfaces) == 0 { 82 // Don't generate networking config. 83 logger.Tracef("no cloud-init network config to generate") 84 return cloudConfig, nil 85 } 86 87 // Render the config first. 88 tmpl, err := template.New("config").Parse(networkConfigTemplate) 89 if err != nil { 90 return nil, errors.Annotate(err, "cannot parse cloud-init network config template") 91 } 92 93 var buf bytes.Buffer 94 if err = tmpl.Execute(&buf, networkConfig.Interfaces); err != nil { 95 return nil, errors.Annotate(err, "cannot render cloud-init network config") 96 } 97 98 // Now add it to cloud-init as a file created early in the boot process. 99 cloudConfig.AddBootTextFile(networkInterfacesFile, buf.String(), 0644) 100 return cloudConfig, nil 101 } 102 103 func cloudInitUserData( 104 machineConfig *cloudinit.MachineConfig, 105 networkConfig *NetworkConfig, 106 ) ([]byte, error) { 107 cloudConfig, err := newCloudInitConfigWithNetworks(networkConfig) 108 udata, err := cloudinit.NewUserdataConfig(machineConfig, cloudConfig) 109 if err != nil { 110 return nil, err 111 } 112 err = udata.Configure() 113 if err != nil { 114 return nil, err 115 } 116 // Run ifconfig to get the addresses of the internal container at least 117 // logged in the host. 118 cloudConfig.AddRunCmd("ifconfig") 119 120 renderer, err := coreCloudinit.NewRenderer(machineConfig.Series) 121 if err != nil { 122 return nil, err 123 } 124 125 data, err := renderer.Render(cloudConfig) 126 if err != nil { 127 return nil, err 128 } 129 return data, nil 130 }