github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cloudconfig/cloudinit/cloudinit_test.go (about) 1 // Copyright 2011, 2012, 2013, 2015 Canonical Ltd. 2 // Copyright 2015 Cloudbase Solutions SRL 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 package cloudinit_test 6 7 import ( 8 "fmt" 9 "testing" 10 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils/packaging" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/cloudconfig/cloudinit" 16 coretesting "github.com/juju/juju/testing" 17 sshtesting "github.com/juju/juju/utils/ssh/testing" 18 ) 19 20 // TODO integration tests, but how? 21 22 type S struct { 23 coretesting.BaseSuite 24 } 25 26 var _ = gc.Suite(S{}) 27 28 func Test1(t *testing.T) { 29 gc.TestingT(t) 30 } 31 32 var ctests = []struct { 33 name string 34 expect map[string]interface{} 35 setOption func(cfg cloudinit.CloudConfig) 36 }{{ 37 "User", 38 map[string]interface{}{"user": "me"}, 39 func(cfg cloudinit.CloudConfig) { 40 cfg.SetUser("me") 41 }, 42 }, { 43 "PackageUpgrade", 44 map[string]interface{}{"package_upgrade": true}, 45 func(cfg cloudinit.CloudConfig) { 46 cfg.SetSystemUpgrade(true) 47 }, 48 }, { 49 "PackageUpdate", 50 map[string]interface{}{"package_update": true}, 51 func(cfg cloudinit.CloudConfig) { 52 cfg.SetSystemUpdate(true) 53 }, 54 }, { 55 "PackageProxy", 56 map[string]interface{}{"apt_proxy": "http://foo.com"}, 57 func(cfg cloudinit.CloudConfig) { 58 cfg.SetPackageProxy("http://foo.com") 59 }, 60 }, { 61 "PackageMirror", 62 map[string]interface{}{"apt_mirror": "http://foo.com"}, 63 func(cfg cloudinit.CloudConfig) { 64 cfg.SetPackageMirror("http://foo.com") 65 }, 66 }, { 67 "DisableEC2Metadata", 68 map[string]interface{}{"disable_ec2_metadata": true}, 69 func(cfg cloudinit.CloudConfig) { 70 cfg.SetDisableEC2Metadata(true) 71 }, 72 }, { 73 "FinalMessage", 74 map[string]interface{}{"final_message": "goodbye"}, 75 func(cfg cloudinit.CloudConfig) { 76 cfg.SetFinalMessage("goodbye") 77 }, 78 }, { 79 "Locale", 80 map[string]interface{}{"locale": "en_us"}, 81 func(cfg cloudinit.CloudConfig) { 82 cfg.SetLocale("en_us") 83 }, 84 }, { 85 "DisableRoot", 86 map[string]interface{}{"disable_root": false}, 87 func(cfg cloudinit.CloudConfig) { 88 cfg.SetDisableRoot(false) 89 }, 90 }, { 91 "SSHAuthorizedKeys", 92 map[string]interface{}{"ssh_authorized_keys": []string{ 93 fmt.Sprintf("%s Juju:user@host", sshtesting.ValidKeyOne.Key), 94 fmt.Sprintf("%s Juju:another@host", sshtesting.ValidKeyTwo.Key), 95 }}, 96 func(cfg cloudinit.CloudConfig) { 97 cfg.AddSSHAuthorizedKeys(sshtesting.ValidKeyOne.Key + " Juju:user@host") 98 cfg.AddSSHAuthorizedKeys(sshtesting.ValidKeyTwo.Key + " another@host") 99 }, 100 }, { 101 "SSHAuthorizedKeys", 102 map[string]interface{}{"ssh_authorized_keys": []string{ 103 fmt.Sprintf("%s Juju:sshkey", sshtesting.ValidKeyOne.Key), 104 fmt.Sprintf("%s Juju:user@host", sshtesting.ValidKeyTwo.Key), 105 fmt.Sprintf("%s Juju:another@host", sshtesting.ValidKeyThree.Key), 106 }}, 107 func(cfg cloudinit.CloudConfig) { 108 cfg.AddSSHAuthorizedKeys("#command\n" + sshtesting.ValidKeyOne.Key) 109 cfg.AddSSHAuthorizedKeys( 110 sshtesting.ValidKeyTwo.Key + " user@host\n# comment\n\n" + 111 sshtesting.ValidKeyThree.Key + " another@host") 112 cfg.AddSSHAuthorizedKeys("") 113 }, 114 }, { 115 "SSHKeys RSAPrivate", 116 map[string]interface{}{"ssh_keys": map[string]interface{}{ 117 "rsa_private": "key1data", 118 }}, 119 func(cfg cloudinit.CloudConfig) { 120 cfg.AddSSHKey(cloudinit.RSAPrivate, "key1data") 121 }, 122 }, { 123 "SSHKeys RSAPublic", 124 map[string]interface{}{"ssh_keys": map[string]interface{}{ 125 "rsa_public": "key2data", 126 }}, 127 func(cfg cloudinit.CloudConfig) { 128 cfg.AddSSHKey(cloudinit.RSAPublic, "key2data") 129 }, 130 }, { 131 "SSHKeys DSAPublic", 132 map[string]interface{}{"ssh_keys": map[string]interface{}{ 133 "dsa_public": "key1data", 134 }}, 135 func(cfg cloudinit.CloudConfig) { 136 cfg.AddSSHKey(cloudinit.DSAPublic, "key1data") 137 }, 138 }, { 139 "SSHKeys DSAPrivate", 140 map[string]interface{}{"ssh_keys": map[string]interface{}{ 141 "dsa_private": "key2data", 142 }}, 143 func(cfg cloudinit.CloudConfig) { 144 cfg.AddSSHKey(cloudinit.DSAPrivate, "key2data") 145 }, 146 }, { 147 "Output", 148 map[string]interface{}{"output": map[string]interface{}{ 149 "all": []string{">foo", "|bar"}, 150 }}, 151 func(cfg cloudinit.CloudConfig) { 152 cfg.SetOutput("all", ">foo", "|bar") 153 }, 154 }, { 155 "Output", 156 map[string]interface{}{"output": map[string]interface{}{ 157 "all": ">foo", 158 }}, 159 func(cfg cloudinit.CloudConfig) { 160 cfg.SetOutput(cloudinit.OutAll, ">foo", "") 161 }, 162 }, { 163 "PackageSources", 164 map[string]interface{}{"apt_sources": []map[string]interface{}{ 165 { 166 "source": "keyName", 167 "key": "someKey", 168 }, 169 }}, 170 func(cfg cloudinit.CloudConfig) { 171 cfg.AddPackageSource(packaging.PackageSource{URL: "keyName", Key: "someKey"}) 172 }, 173 }, { 174 "PackageSources with preferences", 175 map[string]interface{}{ 176 "apt_sources": []map[string]interface{}{ 177 { 178 "source": "keyName", 179 "key": "someKey", 180 }, 181 }, 182 "bootcmd": []string{ 183 "install -D -m 644 /dev/null '/some/path'", 184 "printf '%s\\n' 'Explanation: test\n" + 185 "Package: *\n" + 186 "Pin: release n=series\n" + 187 "Pin-Priority: 123\n" + 188 "' > '/some/path'", 189 }, 190 }, 191 func(cfg cloudinit.CloudConfig) { 192 prefs := packaging.PackagePreferences{ 193 Path: "/some/path", 194 Explanation: "test", 195 Package: "*", 196 Pin: "release n=series", 197 Priority: 123, 198 } 199 cfg.AddPackageSource(packaging.PackageSource{URL: "keyName", Key: "someKey"}) 200 cfg.AddPackagePreferences(prefs) 201 }, 202 }, { 203 "Packages", 204 map[string]interface{}{"packages": []string{ 205 "juju", 206 "ubuntu", 207 }}, 208 func(cfg cloudinit.CloudConfig) { 209 cfg.AddPackage("juju") 210 cfg.AddPackage("ubuntu") 211 }, 212 }, { 213 "Packages on precise, needing cloud-tools", 214 map[string]interface{}{"packages": []string{ 215 // Regular packages (not needing the cloud-tools archive) 216 "juju", 217 "curl", 218 // The following need to be among the list of cloud-tools 219 // packages (see cloudArchivePackagesUbuntu in juju/utils 220 // repo). 221 "--target-release", "precise-updates/cloud-tools", "cloud-utils", 222 "--target-release", "precise-updates/cloud-tools", "cloud-image-utils", 223 // Other regular packages. 224 "ubuntu", 225 }}, 226 func(cfg cloudinit.CloudConfig) { 227 cfg.AddPackage("juju") 228 cfg.AddPackage("curl") 229 // cloud-tools packages need to appear in the list of packages 230 // after the "--target-release" and 231 // "precise-updates/cloud-tools" lines to work around the 232 // broken 0.6.3 cloud-init on precise. cloud-init 0.6.3 233 // concatenates all space-separated arguments for each entry 234 // in the packages list, and then escapes the result in single 235 // quotes, which in turn leads to incorrectly rendered apt-get 236 // command (e.g. instead of "apt-get install --target-release 237 // foo/bar package" 0.6.3 will try to execute "apt-get install 238 // '--target-release foo/bar package'".). See bug #1424777 for 239 // more info. 240 cfg.AddPackage("--target-release") 241 cfg.AddPackage("precise-updates/cloud-tools") 242 cfg.AddPackage("cloud-utils") 243 244 cfg.AddPackage("--target-release") 245 cfg.AddPackage("precise-updates/cloud-tools") 246 cfg.AddPackage("cloud-image-utils") 247 248 cfg.AddPackage("ubuntu") 249 }, 250 }, { 251 "BootCmd", 252 map[string]interface{}{"bootcmd": []string{ 253 "ls > /dev", 254 "ls >with space", 255 }}, 256 func(cfg cloudinit.CloudConfig) { 257 cfg.AddBootCmd("ls > /dev") 258 cfg.AddBootCmd("ls >with space") 259 }, 260 }, { 261 "Mounts", 262 map[string]interface{}{"mounts": [][]string{ 263 {"x", "y"}, 264 {"z", "w"}, 265 }}, 266 func(cfg cloudinit.CloudConfig) { 267 cfg.AddMount("x", "y") 268 cfg.AddMount("z", "w") 269 }, 270 }, { 271 "Attr", 272 map[string]interface{}{"arbitraryAttr": "someValue"}, 273 func(cfg cloudinit.CloudConfig) { 274 cfg.SetAttr("arbitraryAttr", "someValue") 275 }, 276 }, { 277 "RunCmd", 278 map[string]interface{}{"runcmd": []string{ 279 "ifconfig", 280 }}, 281 func(cfg cloudinit.CloudConfig) { 282 cfg.AddRunCmd("ifconfig") 283 }, 284 }, { 285 "AddScripts", 286 map[string]interface{}{"runcmd": []string{ 287 "echo 'Hello World'", 288 "ifconfig", 289 }}, 290 func(cfg cloudinit.CloudConfig) { 291 cfg.AddScripts( 292 "echo 'Hello World'", 293 "ifconfig", 294 ) 295 }, 296 }, { 297 "AddTextFile", 298 map[string]interface{}{"runcmd": []string{ 299 "install -D -m 644 /dev/null '/etc/apt/apt.conf.d/99proxy'", 300 "printf '%s\\n' '\"Acquire::http::Proxy \"http://10.0.3.1:3142\";' > '/etc/apt/apt.conf.d/99proxy'", 301 }}, 302 func(cfg cloudinit.CloudConfig) { 303 cfg.AddRunTextFile( 304 "/etc/apt/apt.conf.d/99proxy", 305 `"Acquire::http::Proxy "http://10.0.3.1:3142";`, 306 0644, 307 ) 308 }, 309 }, { 310 "AddBinaryFile", 311 map[string]interface{}{"runcmd": []string{ 312 "install -D -m 644 /dev/null '/dev/nonsense'", 313 "printf %s AAECAw== | base64 -d > '/dev/nonsense'", 314 }}, 315 func(cfg cloudinit.CloudConfig) { 316 cfg.AddRunBinaryFile( 317 "/dev/nonsense", 318 []byte{0, 1, 2, 3}, 319 0644, 320 ) 321 }, 322 }, { 323 "AddBootTextFile", 324 map[string]interface{}{"bootcmd": []string{ 325 "install -D -m 644 /dev/null '/etc/apt/apt.conf.d/99proxy'", 326 "printf '%s\\n' '\"Acquire::http::Proxy \"http://10.0.3.1:3142\";' > '/etc/apt/apt.conf.d/99proxy'", 327 }}, 328 func(cfg cloudinit.CloudConfig) { 329 cfg.AddBootTextFile( 330 "/etc/apt/apt.conf.d/99proxy", 331 `"Acquire::http::Proxy "http://10.0.3.1:3142";`, 332 0644, 333 ) 334 }, 335 }, 336 } 337 338 func (S) TestOutput(c *gc.C) { 339 for i, t := range ctests { 340 c.Logf("test %d: %s", i, t.name) 341 cfg, err := cloudinit.New("precise") 342 c.Assert(err, jc.ErrorIsNil) 343 t.setOption(cfg) 344 data, err := cfg.RenderYAML() 345 c.Assert(err, jc.ErrorIsNil) 346 c.Assert(data, gc.NotNil) 347 c.Assert(string(data), jc.YAMLEquals, t.expect) 348 data, err = cfg.RenderYAML() 349 c.Assert(err, jc.ErrorIsNil) 350 c.Assert(data, gc.NotNil) 351 c.Assert(string(data), jc.YAMLEquals, t.expect) 352 } 353 } 354 355 func (S) TestRunCmds(c *gc.C) { 356 cfg, err := cloudinit.New("precise") 357 c.Assert(err, jc.ErrorIsNil) 358 c.Assert(cfg.RunCmds(), gc.HasLen, 0) 359 cfg.AddScripts("a", "b") 360 cfg.AddRunCmd("e") 361 c.Assert(cfg.RunCmds(), gc.DeepEquals, []string{ 362 "a", "b", "e", 363 }) 364 } 365 366 func (S) TestPackages(c *gc.C) { 367 cfg, err := cloudinit.New("precise") 368 c.Assert(err, jc.ErrorIsNil) 369 c.Assert(cfg.Packages(), gc.HasLen, 0) 370 cfg.AddPackage("a b c") 371 cfg.AddPackage("d!") 372 expectedPackages := []string{"a b c", "d!"} 373 c.Assert(cfg.Packages(), gc.DeepEquals, expectedPackages) 374 } 375 376 func (S) TestSetOutput(c *gc.C) { 377 type test struct { 378 kind cloudinit.OutputKind 379 stdout string 380 stderr string 381 } 382 tests := []test{{ 383 cloudinit.OutAll, "a", "", 384 }, { 385 cloudinit.OutAll, "", "b", 386 }, { 387 cloudinit.OutInit, "a", "b", 388 }, { 389 cloudinit.OutAll, "a", "b", 390 }, { 391 cloudinit.OutAll, "", "", 392 }} 393 394 cfg, err := cloudinit.New("trusty") 395 c.Assert(err, jc.ErrorIsNil) 396 stdout, stderr := cfg.Output(cloudinit.OutAll) 397 c.Assert(stdout, gc.Equals, "") 398 c.Assert(stderr, gc.Equals, "") 399 for i, t := range tests { 400 c.Logf("test %d: %+v", i, t) 401 cfg.SetOutput(t.kind, t.stdout, t.stderr) 402 stdout, stderr = cfg.Output(t.kind) 403 c.Assert(stdout, gc.Equals, t.stdout) 404 c.Assert(stderr, gc.Equals, t.stderr) 405 } 406 } 407 408 func (S) TestWindowsRender(c *gc.C) { 409 compareOutput := "#ps1_sysnative\r\n\r\npowershell" 410 cfg, err := cloudinit.New("win8") 411 c.Assert(err, jc.ErrorIsNil) 412 cfg.AddRunCmd("powershell") 413 data, err := cfg.RenderYAML() 414 c.Assert(err, jc.ErrorIsNil) 415 c.Assert(data, gc.NotNil) 416 c.Assert(string(data), gc.Equals, compareOutput, gc.Commentf("test %q output differs", "windows renderer")) 417 }