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  }