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 }