github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  	"fmt"
     9  	"path/filepath"
    10  	"strings"
    11  	stdtesting "testing"
    12  
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/yaml.v2"
    16  
    17  	"github.com/juju/juju/cloudconfig/cloudinit"
    18  	"github.com/juju/juju/cloudconfig/containerinit"
    19  	"github.com/juju/juju/container"
    20  	containertesting "github.com/juju/juju/container/testing"
    21  	"github.com/juju/juju/network"
    22  	"github.com/juju/juju/testing"
    23  )
    24  
    25  func Test(t *stdtesting.T) {
    26  	gc.TestingT(t)
    27  }
    28  
    29  type UserDataSuite struct {
    30  	testing.BaseSuite
    31  
    32  	networkInterfacesFile       string
    33  	systemNetworkInterfacesFile string
    34  
    35  	fakeInterfaces []network.InterfaceInfo
    36  
    37  	expectedSampleConfig     string
    38  	expectedSampleUserData   string
    39  	expectedFallbackConfig   string
    40  	expectedFallbackUserData string
    41  }
    42  
    43  var _ = gc.Suite(&UserDataSuite{})
    44  
    45  func (s *UserDataSuite) SetUpTest(c *gc.C) {
    46  	s.BaseSuite.SetUpTest(c)
    47  	s.networkInterfacesFile = filepath.Join(c.MkDir(), "juju-interfaces")
    48  	s.systemNetworkInterfacesFile = filepath.Join(c.MkDir(), "system-interfaces")
    49  	s.fakeInterfaces = []network.InterfaceInfo{{
    50  		InterfaceName:    "any0",
    51  		CIDR:             "0.1.2.0/24",
    52  		ConfigType:       network.ConfigStatic,
    53  		NoAutoStart:      false,
    54  		Address:          network.NewAddress("0.1.2.3"),
    55  		DNSServers:       network.NewAddresses("ns1.invalid", "ns2.invalid"),
    56  		DNSSearchDomains: []string{"foo", "bar"},
    57  		GatewayAddress:   network.NewAddress("0.1.2.1"),
    58  		MACAddress:       "aa:bb:cc:dd:ee:f0",
    59  	}, {
    60  		InterfaceName:    "any1",
    61  		CIDR:             "0.2.2.0/24",
    62  		ConfigType:       network.ConfigStatic,
    63  		NoAutoStart:      false,
    64  		Address:          network.NewAddress("0.2.2.4"),
    65  		DNSServers:       network.NewAddresses("ns1.invalid", "ns2.invalid"),
    66  		DNSSearchDomains: []string{"foo", "bar"},
    67  		GatewayAddress:   network.NewAddress("0.2.2.1"),
    68  		MACAddress:       "aa:bb:cc:dd:ee:f1",
    69  	}, {
    70  		InterfaceName: "any2",
    71  		ConfigType:    network.ConfigDHCP,
    72  		NoAutoStart:   true,
    73  	}, {
    74  		InterfaceName: "any3",
    75  		ConfigType:    network.ConfigDHCP,
    76  		NoAutoStart:   false,
    77  	}, {
    78  		InterfaceName: "any4",
    79  		ConfigType:    network.ConfigManual,
    80  		NoAutoStart:   true,
    81  	}}
    82  	s.expectedSampleConfig = `
    83  auto any0 any1 any3 lo
    84  
    85  iface lo inet loopback
    86    dns-nameservers ns1.invalid ns2.invalid
    87    dns-search bar foo
    88  
    89  iface any0 inet static
    90    address 0.1.2.3/24
    91    gateway 0.1.2.1
    92  
    93  iface any1 inet static
    94    address 0.2.2.4/24
    95  
    96  iface any2 inet dhcp
    97  
    98  iface any3 inet dhcp
    99  
   100  iface any4 inet manual
   101  `
   102  	s.expectedSampleUserData = `
   103  #cloud-config
   104  bootcmd:
   105  - install -D -m 644 /dev/null '%[1]s'
   106  - |-
   107    printf '%%s\n' '
   108    auto any0 any1 any3 lo
   109  
   110    iface lo inet loopback
   111      dns-nameservers ns1.invalid ns2.invalid
   112      dns-search bar foo
   113  
   114    iface any0 inet static
   115      address 0.1.2.3/24
   116      gateway 0.1.2.1
   117  
   118    iface any1 inet static
   119      address 0.2.2.4/24
   120  
   121    iface any2 inet dhcp
   122  
   123    iface any3 inet dhcp
   124  
   125    iface any4 inet manual
   126    ' > '%[1]s'
   127  runcmd:
   128  - |-
   129    if [ -f %[1]s ]; then
   130        ifdown -a
   131        sleep 1.5
   132        if ifup -a --interfaces=%[1]s; then
   133            cp %[2]s %[2]s-orig
   134            cp %[1]s %[2]s
   135        else
   136            ifup -a
   137        fi
   138    fi
   139  `[1:]
   140  
   141  	s.expectedFallbackConfig = `
   142  auto eth0 lo
   143  
   144  iface lo inet loopback
   145  
   146  iface eth0 inet dhcp
   147  `
   148  	s.expectedFallbackUserData = `
   149  #cloud-config
   150  bootcmd:
   151  - install -D -m 644 /dev/null '%[1]s'
   152  - |-
   153    printf '%%s\n' '
   154    auto eth0 lo
   155  
   156    iface lo inet loopback
   157  
   158    iface eth0 inet dhcp
   159    ' > '%[1]s'
   160  runcmd:
   161  - |-
   162    if [ -f %[1]s ]; then
   163        ifdown -a
   164        sleep 1.5
   165        if ifup -a --interfaces=%[1]s; then
   166            cp %[2]s %[2]s-orig
   167            cp %[1]s %[2]s
   168        else
   169            ifup -a
   170        fi
   171    fi
   172  `[1:]
   173  
   174  	s.PatchValue(containerinit.NetworkInterfacesFile, s.networkInterfacesFile)
   175  	s.PatchValue(containerinit.SystemNetworkInterfacesFile, s.systemNetworkInterfacesFile)
   176  }
   177  
   178  func (s *UserDataSuite) TestGenerateNetworkConfig(c *gc.C) {
   179  	data, err := containerinit.GenerateNetworkConfig(nil)
   180  	c.Assert(err, gc.ErrorMatches, "missing container network config")
   181  	c.Assert(data, gc.Equals, "")
   182  
   183  	netConfig := container.BridgeNetworkConfig("foo", 0, nil)
   184  	data, err = containerinit.GenerateNetworkConfig(netConfig)
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	c.Assert(data, gc.Equals, s.expectedFallbackConfig)
   187  
   188  	// Test with all interface types.
   189  	netConfig = container.BridgeNetworkConfig("foo", 0, s.fakeInterfaces)
   190  	data, err = containerinit.GenerateNetworkConfig(netConfig)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	c.Assert(data, gc.Equals, s.expectedSampleConfig)
   193  }
   194  
   195  func (s *UserDataSuite) TestNewCloudInitConfigWithNetworksSampleConfig(c *gc.C) {
   196  	netConfig := container.BridgeNetworkConfig("foo", 0, s.fakeInterfaces)
   197  	cloudConf, err := containerinit.NewCloudInitConfigWithNetworks("quantal", netConfig)
   198  	c.Assert(err, jc.ErrorIsNil)
   199  	c.Assert(cloudConf, gc.NotNil)
   200  
   201  	expected := fmt.Sprintf(s.expectedSampleUserData, s.networkInterfacesFile, s.systemNetworkInterfacesFile)
   202  	assertUserData(c, cloudConf, expected)
   203  }
   204  
   205  func (s *UserDataSuite) TestNewCloudInitConfigWithNetworksFallbackConfig(c *gc.C) {
   206  	netConfig := container.BridgeNetworkConfig("foo", 0, nil)
   207  	cloudConf, err := containerinit.NewCloudInitConfigWithNetworks("quantal", netConfig)
   208  	c.Assert(err, jc.ErrorIsNil)
   209  	c.Assert(cloudConf, gc.NotNil)
   210  	expected := fmt.Sprintf(s.expectedFallbackUserData, s.networkInterfacesFile, s.systemNetworkInterfacesFile)
   211  	assertUserData(c, cloudConf, expected)
   212  }
   213  
   214  func CloudInitDataExcludingOutputSection(data string) []string {
   215  	// Extract the "#cloud-config" header and all lines between
   216  	// from the "bootcmd" section up to (but not including) the
   217  	// "output" sections to match against expected. But we cannot
   218  	// possibly handle all the /other/ output that may be added by
   219  	// CloudInitUserData() in the future, so we also truncate at
   220  	// the first runcmd which now happens to include the runcmd's
   221  	// added for raising the network interfaces captured in
   222  	// expectedFallbackUserData. However, the other tests above do
   223  	// check for that output.
   224  
   225  	var linesToMatch []string
   226  	seenBootcmd := false
   227  	for _, line := range strings.Split(string(data), "\n") {
   228  		if strings.HasPrefix(line, "#cloud-config") {
   229  			linesToMatch = append(linesToMatch, line)
   230  			continue
   231  		}
   232  
   233  		if strings.HasPrefix(line, "bootcmd:") {
   234  			seenBootcmd = true
   235  		}
   236  
   237  		if strings.HasPrefix(line, "output:") && seenBootcmd {
   238  			break
   239  		}
   240  
   241  		if seenBootcmd {
   242  			linesToMatch = append(linesToMatch, line)
   243  		}
   244  	}
   245  
   246  	return linesToMatch
   247  }
   248  
   249  // TestCloudInitUserDataNoNetworkConfig tests that no network-interfaces, or
   250  // related data, appear in user-data when no networkConfig is passed to
   251  // CloudInitUserData.
   252  func (s *UserDataSuite) TestCloudInitUserDataNoNetworkConfig(c *gc.C) {
   253  	instanceConfig, err := containertesting.MockMachineConfig("1/lxd/0")
   254  	c.Assert(err, jc.ErrorIsNil)
   255  	data, err := containerinit.CloudInitUserData(instanceConfig, nil)
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	c.Assert(data, gc.NotNil)
   258  
   259  	linesToMatch := CloudInitDataExcludingOutputSection(string(data))
   260  
   261  	c.Assert(strings.Join(linesToMatch, "\n"), gc.Equals, "#cloud-config")
   262  }
   263  
   264  func (s *UserDataSuite) TestCloudInitUserDataFallbackConfig(c *gc.C) {
   265  	instanceConfig, err := containertesting.MockMachineConfig("1/lxd/0")
   266  	c.Assert(err, jc.ErrorIsNil)
   267  	networkConfig := container.BridgeNetworkConfig("foo", 0, nil)
   268  	data, err := containerinit.CloudInitUserData(instanceConfig, networkConfig)
   269  	c.Assert(err, jc.ErrorIsNil)
   270  	c.Assert(data, gc.NotNil)
   271  
   272  	linesToMatch := CloudInitDataExcludingOutputSection(string(data))
   273  
   274  	expected := fmt.Sprintf(s.expectedFallbackUserData, s.networkInterfacesFile, s.systemNetworkInterfacesFile)
   275  	var expectedLinesToMatch []string
   276  
   277  	for _, line := range strings.Split(expected, "\n") {
   278  		if strings.HasPrefix(line, "runcmd:") {
   279  			break
   280  		}
   281  		expectedLinesToMatch = append(expectedLinesToMatch, line)
   282  	}
   283  
   284  	expectedLinesToMatch = append(expectedLinesToMatch, "manage_etc_hosts: true")
   285  
   286  	c.Assert(strings.Join(linesToMatch, "\n")+"\n", gc.Equals, strings.Join(expectedLinesToMatch, "\n")+"\n")
   287  }
   288  
   289  func (s *UserDataSuite) TestCloudInitUserDataFallbackConfigWithContainerHostname(c *gc.C) {
   290  	instanceConfig, err := containertesting.MockMachineConfig("1/lxd/0")
   291  	instanceConfig.MachineContainerHostname = "lxdhostname"
   292  	c.Assert(err, jc.ErrorIsNil)
   293  	networkConfig := container.BridgeNetworkConfig("foo", 0, nil)
   294  	data, err := containerinit.CloudInitUserData(instanceConfig, networkConfig)
   295  	c.Assert(err, jc.ErrorIsNil)
   296  	c.Assert(data, gc.NotNil)
   297  
   298  	linesToMatch := CloudInitDataExcludingOutputSection(string(data))
   299  
   300  	expected := fmt.Sprintf(s.expectedFallbackUserData, s.networkInterfacesFile, s.systemNetworkInterfacesFile)
   301  	var expectedLinesToMatch []string
   302  
   303  	for _, line := range strings.Split(expected, "\n") {
   304  		if strings.HasPrefix(line, "runcmd:") {
   305  			break
   306  		}
   307  		expectedLinesToMatch = append(expectedLinesToMatch, line)
   308  	}
   309  
   310  	expectedLinesToMatch = append(expectedLinesToMatch, "hostname: lxdhostname")
   311  	expectedLinesToMatch = append(expectedLinesToMatch, "manage_etc_hosts: true")
   312  
   313  	c.Assert(strings.Join(linesToMatch, "\n")+"\n", gc.Equals, strings.Join(expectedLinesToMatch, "\n")+"\n")
   314  }
   315  
   316  func assertUserData(c *gc.C, cloudConf cloudinit.CloudConfig, expected string) {
   317  	data, err := cloudConf.RenderYAML()
   318  	c.Assert(err, jc.ErrorIsNil)
   319  	c.Assert(string(data), gc.Equals, expected)
   320  
   321  	// Make sure it's valid YAML as well.
   322  	out := make(map[string]interface{})
   323  	err = yaml.Unmarshal(data, &out)
   324  	c.Assert(err, jc.ErrorIsNil)
   325  	if len(cloudConf.BootCmds()) > 0 {
   326  		outcmds := out["bootcmd"].([]interface{})
   327  		confcmds := cloudConf.BootCmds()
   328  		c.Assert(len(outcmds), gc.Equals, len(confcmds))
   329  		for i, _ := range outcmds {
   330  			c.Assert(outcmds[i].(string), gc.Equals, confcmds[i])
   331  		}
   332  	} else {
   333  		c.Assert(out["bootcmd"], gc.IsNil)
   334  	}
   335  }