github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cloudconfig/containerinit/container_userdata_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Copyright 2015 Cloudbase Solutions SRL
     3  // Licensed under the AGPLv3, see LICENCE file for details.
     4  
     5  package containerinit_test
     6  
     7  import (
     8  	"path/filepath"
     9  	"strings"
    10  	stdtesting "testing"
    11  
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/yaml.v2"
    15  
    16  	"github.com/juju/juju/cloudconfig/cloudinit"
    17  	"github.com/juju/juju/cloudconfig/containerinit"
    18  	"github.com/juju/juju/container"
    19  	containertesting "github.com/juju/juju/container/testing"
    20  	"github.com/juju/juju/feature"
    21  	"github.com/juju/juju/network"
    22  	"github.com/juju/juju/service"
    23  	systemdtesting "github.com/juju/juju/service/systemd/testing"
    24  	"github.com/juju/juju/testing"
    25  )
    26  
    27  func Test(t *stdtesting.T) {
    28  	gc.TestingT(t)
    29  }
    30  
    31  type UserDataSuite struct {
    32  	testing.BaseSuite
    33  
    34  	networkInterfacesFile string
    35  	fakeInterfaces        []network.InterfaceInfo
    36  	expectedNetConfig     string
    37  }
    38  
    39  var _ = gc.Suite(&UserDataSuite{})
    40  
    41  func (s *UserDataSuite) SetUpTest(c *gc.C) {
    42  	s.BaseSuite.SetUpTest(c)
    43  	s.networkInterfacesFile = filepath.Join(c.MkDir(), "interfaces")
    44  	s.fakeInterfaces = []network.InterfaceInfo{{
    45  		InterfaceName:    "eth0",
    46  		CIDR:             "0.1.2.0/24",
    47  		ConfigType:       network.ConfigStatic,
    48  		NoAutoStart:      false,
    49  		Address:          network.NewAddress("0.1.2.3"),
    50  		DNSServers:       network.NewAddresses("ns1.invalid", "ns2.invalid"),
    51  		DNSSearchDomains: []string{"foo", "bar"},
    52  		GatewayAddress:   network.NewAddress("0.1.2.1"),
    53  		MACAddress:       "aa:bb:cc:dd:ee:f0",
    54  	}, {
    55  		InterfaceName:    "eth1",
    56  		CIDR:             "0.1.2.0/24",
    57  		ConfigType:       network.ConfigStatic,
    58  		NoAutoStart:      false,
    59  		Address:          network.NewAddress("0.1.2.4"),
    60  		DNSServers:       network.NewAddresses("ns1.invalid", "ns2.invalid"),
    61  		DNSSearchDomains: []string{"foo", "bar"},
    62  		GatewayAddress:   network.NewAddress("0.1.2.1"),
    63  		MACAddress:       "aa:bb:cc:dd:ee:f0",
    64  	}, {
    65  		InterfaceName: "eth2",
    66  		ConfigType:    network.ConfigDHCP,
    67  		NoAutoStart:   true,
    68  	}}
    69  	s.expectedNetConfig = `
    70  auto lo
    71  iface lo inet loopback
    72  
    73  auto eth0
    74  iface eth0 inet manual
    75    dns-nameservers ns1.invalid ns2.invalid
    76    dns-search foo bar
    77    pre-up ip address add 0.1.2.3/24 dev eth0 || true
    78    up ip route replace 0.1.2.0/24 dev eth0 || true
    79    down ip route del 0.1.2.0/24 dev eth0 || true
    80    post-down address del 0.1.2.3/24 dev eth0 || true
    81    up ip route replace default via 0.1.2.1 || true
    82    down ip route del default via 0.1.2.1 || true
    83  
    84  auto eth1
    85  iface eth1 inet manual
    86    dns-nameservers ns1.invalid ns2.invalid
    87    dns-search foo bar
    88    pre-up ip address add 0.1.2.4/24 dev eth1 || true
    89    up ip route replace 0.1.2.0/24 dev eth1 || true
    90    down ip route del 0.1.2.0/24 dev eth1 || true
    91    post-down address del 0.1.2.4/24 dev eth1 || true
    92  
    93  iface eth2 inet manual
    94    pre-up ip address add  dev eth2 || true
    95    up ip route replace  dev eth2 || true
    96    down ip route del  dev eth2 || true
    97    post-down address del  dev eth2 || true
    98  
    99  `
   100  	s.PatchValue(containerinit.NetworkInterfacesFile, s.networkInterfacesFile)
   101  }
   102  
   103  func (s *UserDataSuite) TestGenerateNetworkConfig(c *gc.C) {
   104  	// No config or no interfaces - no error, but also noting to generate.
   105  	data, err := containerinit.GenerateNetworkConfig(nil)
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	c.Assert(data, gc.HasLen, 0)
   108  	netConfig := container.BridgeNetworkConfig("foo", 0, nil)
   109  	data, err = containerinit.GenerateNetworkConfig(netConfig)
   110  	c.Assert(err, jc.ErrorIsNil)
   111  	c.Assert(data, gc.HasLen, 0)
   112  
   113  	// Test with all interface types.
   114  	netConfig = container.BridgeNetworkConfig("foo", 0, s.fakeInterfaces)
   115  	data, err = containerinit.GenerateNetworkConfig(netConfig)
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	c.Assert(data, gc.Equals, s.expectedNetConfig)
   118  }
   119  
   120  func (s *UserDataSuite) TestNewCloudInitConfigWithNetworks(c *gc.C) {
   121  	netConfig := container.BridgeNetworkConfig("foo", 0, s.fakeInterfaces)
   122  	cloudConf, err := containerinit.NewCloudInitConfigWithNetworks("quantal", netConfig)
   123  	c.Assert(err, jc.ErrorIsNil)
   124  	// We need to indent expectNetConfig to make it valid YAML,
   125  	// dropping the last new line and using unindented blank lines.
   126  	lines := strings.Split(s.expectedNetConfig, "\n")
   127  	indentedNetConfig := strings.Join(lines[:len(lines)-2], "\n  ")
   128  	indentedNetConfig = strings.Replace(indentedNetConfig, "\n  \n", "\n\n", -1) + "\n"
   129  	expected := `
   130  #cloud-config
   131  bootcmd:
   132  - install -D -m 644 /dev/null '`[1:] + s.networkInterfacesFile + `'
   133  - |-
   134    printf '%s\n' '` + indentedNetConfig + `
   135    ' > '` + s.networkInterfacesFile + `'
   136  runcmd:
   137  - ifup -a || true
   138  `
   139  	assertUserData(c, cloudConf, expected)
   140  }
   141  
   142  func (s *UserDataSuite) TestNewCloudInitConfigWithNetworksNoConfig(c *gc.C) {
   143  	netConfig := container.BridgeNetworkConfig("foo", 0, nil)
   144  	cloudConf, err := containerinit.NewCloudInitConfigWithNetworks("quantal", netConfig)
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	expected := "#cloud-config\n{}\n"
   147  	assertUserData(c, cloudConf, expected)
   148  }
   149  
   150  func (s *UserDataSuite) TestCloudInitUserData(c *gc.C) {
   151  	instanceConfig, err := containertesting.MockMachineConfig("1/lxc/0")
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	networkConfig := container.BridgeNetworkConfig("foo", 0, nil)
   154  	data, err := containerinit.CloudInitUserData(instanceConfig, networkConfig)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	// No need to test the exact contents here, as they are already
   157  	// tested separately.
   158  	c.Assert(string(data), jc.HasPrefix, "#cloud-config\n")
   159  }
   160  
   161  func assertUserData(c *gc.C, cloudConf cloudinit.CloudConfig, expected string) {
   162  	data, err := cloudConf.RenderYAML()
   163  	c.Assert(err, jc.ErrorIsNil)
   164  	c.Assert(string(data), gc.Equals, expected)
   165  	// Make sure it's valid YAML as well.
   166  	out := make(map[string]interface{})
   167  	err = yaml.Unmarshal(data, &out)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	if len(cloudConf.BootCmds()) > 0 {
   170  		outcmds := out["bootcmd"].([]interface{})
   171  		confcmds := cloudConf.BootCmds()
   172  		c.Assert(len(outcmds), gc.Equals, len(confcmds))
   173  		for i, _ := range outcmds {
   174  			c.Assert(outcmds[i].(string), gc.Equals, confcmds[i])
   175  		}
   176  	} else {
   177  		c.Assert(out["bootcmd"], gc.IsNil)
   178  	}
   179  }
   180  
   181  func (s *UserDataSuite) TestShutdownInitCommandsUpstart(c *gc.C) {
   182  	s.SetFeatureFlags(feature.AddressAllocation)
   183  	cmds, err := containerinit.ShutdownInitCommands(service.InitSystemUpstart, "trusty")
   184  	c.Assert(err, jc.ErrorIsNil)
   185  
   186  	filename := "/etc/init/juju-template-restart.conf"
   187  	script := `
   188  description "juju shutdown job"
   189  author "Juju Team <juju@lists.ubuntu.com>"
   190  start on stopped cloud-final
   191  
   192  script
   193    /bin/cat > /etc/network/interfaces << EOC
   194  # loopback interface
   195  auto lo
   196  iface lo inet loopback
   197  
   198  # primary interface
   199  auto eth0
   200  iface eth0 inet dhcp
   201  EOC
   202    /bin/rm -fr /var/lib/dhcp/dhclient* /var/log/cloud-init*.log
   203    /sbin/shutdown -h now
   204  end script
   205  
   206  post-stop script
   207    rm /etc/init/juju-template-restart.conf
   208  end script
   209  `[1:]
   210  	c.Check(cmds, gc.HasLen, 1)
   211  	testing.CheckWriteFileCommand(c, cmds[0], filename, script, nil)
   212  }
   213  
   214  func (s *UserDataSuite) TestShutdownInitCommandsSystemd(c *gc.C) {
   215  	s.SetFeatureFlags(feature.AddressAllocation)
   216  	commands, err := containerinit.ShutdownInitCommands(service.InitSystemSystemd, "vivid")
   217  	c.Assert(err, jc.ErrorIsNil)
   218  
   219  	test := systemdtesting.WriteConfTest{
   220  		Service: "juju-template-restart",
   221  		DataDir: "/var/lib/juju",
   222  		Expected: `
   223  [Unit]
   224  Description=juju shutdown job
   225  After=syslog.target
   226  After=network.target
   227  After=systemd-user-sessions.service
   228  After=cloud-config.target
   229  
   230  [Service]
   231  ExecStart=/var/lib/juju/init/juju-template-restart/exec-start.sh
   232  ExecStopPost=/bin/systemctl disable juju-template-restart.service
   233  
   234  [Install]
   235  WantedBy=multi-user.target
   236  `[1:],
   237  		Script: `
   238  /bin/cat > /etc/network/interfaces << EOC
   239  # loopback interface
   240  auto lo
   241  iface lo inet loopback
   242  
   243  # primary interface
   244  auto eth0
   245  iface eth0 inet dhcp
   246  EOC
   247    /bin/rm -fr /var/lib/dhcp/dhclient* /var/log/cloud-init*.log
   248    /sbin/shutdown -h now`[1:],
   249  	}
   250  	test.CheckInstallAndStartCommands(c, commands)
   251  }