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