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  }