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  }