launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/environs/cloudinit_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package environs_test
     5  
     6  import (
     7  	"time"
     8  
     9  	gc "launchpad.net/gocheck"
    10  	"launchpad.net/goyaml"
    11  
    12  	"launchpad.net/juju-core/agent"
    13  	"launchpad.net/juju-core/cert"
    14  	"launchpad.net/juju-core/constraints"
    15  	"launchpad.net/juju-core/environs"
    16  	"launchpad.net/juju-core/environs/cloudinit"
    17  	"launchpad.net/juju-core/environs/config"
    18  	"launchpad.net/juju-core/juju/osenv"
    19  	"launchpad.net/juju-core/provider/dummy"
    20  	"launchpad.net/juju-core/state"
    21  	"launchpad.net/juju-core/state/api"
    22  	"launchpad.net/juju-core/testing"
    23  	jc "launchpad.net/juju-core/testing/checkers"
    24  	"launchpad.net/juju-core/testing/testbase"
    25  	"launchpad.net/juju-core/tools"
    26  	"launchpad.net/juju-core/utils"
    27  	"launchpad.net/juju-core/version"
    28  )
    29  
    30  // dummySampleConfig returns the dummy sample config without
    31  // the state server configured.
    32  // will not run a state server.
    33  func dummySampleConfig() testing.Attrs {
    34  	return dummy.SampleConfig().Merge(testing.Attrs{
    35  		"state-server": false,
    36  	})
    37  }
    38  
    39  type CloudInitSuite struct {
    40  	testbase.LoggingSuite
    41  }
    42  
    43  var _ = gc.Suite(&CloudInitSuite{})
    44  
    45  func (s *CloudInitSuite) TestFinishInstanceConfig(c *gc.C) {
    46  	attrs := dummySampleConfig().Merge(testing.Attrs{
    47  		"authorized-keys": "we-are-the-keys",
    48  	})
    49  	cfg, err := config.New(config.NoDefaults, attrs)
    50  	c.Assert(err, gc.IsNil)
    51  	mcfg := &cloudinit.MachineConfig{
    52  		StateInfo: &state.Info{Tag: "not touched"},
    53  		APIInfo:   &api.Info{Tag: "not touched"},
    54  	}
    55  	err = environs.FinishMachineConfig(mcfg, cfg, constraints.Value{})
    56  	c.Assert(err, gc.IsNil)
    57  	c.Assert(mcfg, gc.DeepEquals, &cloudinit.MachineConfig{
    58  		AuthorizedKeys: "we-are-the-keys",
    59  		AgentEnvironment: map[string]string{
    60  			agent.ProviderType:  "dummy",
    61  			agent.ContainerType: "",
    62  		},
    63  		StateInfo: &state.Info{Tag: "not touched"},
    64  		APIInfo:   &api.Info{Tag: "not touched"},
    65  		DisableSSLHostnameVerification: false,
    66  		SyslogPort:                     2345,
    67  	})
    68  }
    69  
    70  func (s *CloudInitSuite) TestFinishMachineConfigNonDefault(c *gc.C) {
    71  	attrs := dummySampleConfig().Merge(testing.Attrs{
    72  		"authorized-keys":           "we-are-the-keys",
    73  		"ssl-hostname-verification": false,
    74  		"syslog-port":               8888,
    75  	})
    76  	cfg, err := config.New(config.NoDefaults, attrs)
    77  	c.Assert(err, gc.IsNil)
    78  	mcfg := &cloudinit.MachineConfig{
    79  		StateInfo: &state.Info{Tag: "not touched"},
    80  		APIInfo:   &api.Info{Tag: "not touched"},
    81  	}
    82  	err = environs.FinishMachineConfig(mcfg, cfg, constraints.Value{})
    83  	c.Assert(err, gc.IsNil)
    84  	c.Assert(mcfg, gc.DeepEquals, &cloudinit.MachineConfig{
    85  		AuthorizedKeys: "we-are-the-keys",
    86  		AgentEnvironment: map[string]string{
    87  			agent.ProviderType:  "dummy",
    88  			agent.ContainerType: "",
    89  		},
    90  		StateInfo: &state.Info{Tag: "not touched"},
    91  		APIInfo:   &api.Info{Tag: "not touched"},
    92  		DisableSSLHostnameVerification: true,
    93  		SyslogPort:                     8888,
    94  	})
    95  }
    96  
    97  func (s *CloudInitSuite) TestFinishBootstrapConfig(c *gc.C) {
    98  	attrs := dummySampleConfig().Merge(testing.Attrs{
    99  		"authorized-keys": "we-are-the-keys",
   100  		"admin-secret":    "lisboan-pork",
   101  		"agent-version":   "1.2.3",
   102  		"state-server":    false,
   103  	})
   104  	cfg, err := config.New(config.NoDefaults, attrs)
   105  	c.Assert(err, gc.IsNil)
   106  	oldAttrs := cfg.AllAttrs()
   107  	mcfg := &cloudinit.MachineConfig{
   108  		StateServer: true,
   109  	}
   110  	cons := constraints.MustParse("mem=1T cpu-power=999999999")
   111  	err = environs.FinishMachineConfig(mcfg, cfg, cons)
   112  	c.Assert(err, gc.IsNil)
   113  	c.Check(mcfg.AuthorizedKeys, gc.Equals, "we-are-the-keys")
   114  	c.Check(mcfg.DisableSSLHostnameVerification, jc.IsFalse)
   115  	password := utils.UserPasswordHash("lisboan-pork", utils.CompatSalt)
   116  	c.Check(mcfg.APIInfo, gc.DeepEquals, &api.Info{
   117  		Password: password, CACert: []byte(testing.CACert),
   118  	})
   119  	c.Check(mcfg.StateInfo, gc.DeepEquals, &state.Info{
   120  		Password: password, CACert: []byte(testing.CACert),
   121  	})
   122  	c.Check(mcfg.StatePort, gc.Equals, cfg.StatePort())
   123  	c.Check(mcfg.APIPort, gc.Equals, cfg.APIPort())
   124  	c.Check(mcfg.Constraints, gc.DeepEquals, cons)
   125  
   126  	oldAttrs["ca-private-key"] = ""
   127  	oldAttrs["admin-secret"] = ""
   128  	c.Check(mcfg.Config.AllAttrs(), gc.DeepEquals, oldAttrs)
   129  	srvCertPEM := mcfg.StateServerCert
   130  	srvKeyPEM := mcfg.StateServerKey
   131  	_, _, err = cert.ParseCertAndKey(srvCertPEM, srvKeyPEM)
   132  	c.Check(err, gc.IsNil)
   133  
   134  	err = cert.Verify(srvCertPEM, []byte(testing.CACert), time.Now())
   135  	c.Assert(err, gc.IsNil)
   136  	err = cert.Verify(srvCertPEM, []byte(testing.CACert), time.Now().AddDate(9, 0, 0))
   137  	c.Assert(err, gc.IsNil)
   138  	err = cert.Verify(srvCertPEM, []byte(testing.CACert), time.Now().AddDate(10, 0, 1))
   139  	c.Assert(err, gc.NotNil)
   140  }
   141  
   142  func (s *CloudInitSuite) TestUserData(c *gc.C) {
   143  	s.testUserData(c, false)
   144  }
   145  
   146  func (s *CloudInitSuite) TestStateServerUserData(c *gc.C) {
   147  	s.testUserData(c, true)
   148  }
   149  
   150  func (*CloudInitSuite) testUserData(c *gc.C, stateServer bool) {
   151  	testJujuHome := c.MkDir()
   152  	defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome))
   153  	tools := &tools.Tools{
   154  		URL:     "http://foo.com/tools/releases/juju1.2.3-linux-amd64.tgz",
   155  		Version: version.MustParseBinary("1.2.3-linux-amd64"),
   156  	}
   157  	envConfig, err := config.New(config.NoDefaults, dummySampleConfig())
   158  	c.Assert(err, gc.IsNil)
   159  
   160  	cfg := &cloudinit.MachineConfig{
   161  		MachineId:       "10",
   162  		MachineNonce:    "5432",
   163  		Tools:           tools,
   164  		StateServerCert: []byte(testing.ServerCert),
   165  		StateServerKey:  []byte(testing.ServerKey),
   166  		StateInfo: &state.Info{
   167  			Addrs:    []string{"127.0.0.1:1234"},
   168  			Password: "pw1",
   169  			CACert:   []byte("CA CERT\n" + testing.CACert),
   170  			Tag:      "machine-10",
   171  		},
   172  		APIInfo: &api.Info{
   173  			Addrs:    []string{"127.0.0.1:1234"},
   174  			Password: "pw2",
   175  			CACert:   []byte("CA CERT\n" + testing.CACert),
   176  			Tag:      "machine-10",
   177  		},
   178  		DataDir:                 environs.DataDir,
   179  		LogDir:                  environs.LogDir,
   180  		CloudInitOutputLog:      environs.CloudInitOutputLog,
   181  		RsyslogConfPath:         environs.RsyslogConfPath,
   182  		Config:                  envConfig,
   183  		StatePort:               envConfig.StatePort(),
   184  		APIPort:                 envConfig.APIPort(),
   185  		SyslogPort:              envConfig.SyslogPort(),
   186  		StateServer:             stateServer,
   187  		AgentEnvironment:        map[string]string{agent.ProviderType: "dummy"},
   188  		AuthorizedKeys:          "wheredidileavemykeys",
   189  		MachineAgentServiceName: "jujud-machine-10",
   190  	}
   191  	script1 := "script1"
   192  	script2 := "script2"
   193  	scripts := []string{script1, script2}
   194  	result, err := environs.ComposeUserData(cfg, scripts...)
   195  	c.Assert(err, gc.IsNil)
   196  
   197  	unzipped, err := utils.Gunzip(result)
   198  	c.Assert(err, gc.IsNil)
   199  
   200  	config := make(map[interface{}]interface{})
   201  	err = goyaml.Unmarshal(unzipped, &config)
   202  	c.Assert(err, gc.IsNil)
   203  
   204  	// The scripts given to userData where added as the first
   205  	// commands to be run.
   206  	runCmd := config["runcmd"].([]interface{})
   207  	c.Check(runCmd[0], gc.Equals, script1)
   208  	c.Check(runCmd[1], gc.Equals, script2)
   209  
   210  	if stateServer {
   211  		// The cloudinit config should have nothing but the basics:
   212  		// SSH authorized keys, the additional runcmds, and log output.
   213  		//
   214  		// Note: the additional runcmds *do* belong here, at least
   215  		// for MAAS. MAAS needs to configure and then bounce the
   216  		// network interfaces, which would sever the SSH connection
   217  		// in the synchronous bootstrap phase.
   218  		c.Check(config, gc.DeepEquals, map[interface{}]interface{}{
   219  			"output": map[interface{}]interface{}{
   220  				"all": "| tee -a /var/log/cloud-init-output.log",
   221  			},
   222  			"runcmd": []interface{}{
   223  				"script1", "script2",
   224  				"set -xe",
   225  				"install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'",
   226  				"printf '%s\\n' '5432' > '/var/lib/juju/nonce.txt'",
   227  			},
   228  			"ssh_authorized_keys": []interface{}{"wheredidileavemykeys"},
   229  		})
   230  	} else {
   231  		// Just check that the cloudinit config looks good,
   232  		// and that there are more runcmds than the additional
   233  		// ones we passed into ComposeUserData.
   234  		c.Check(config["apt_upgrade"], gc.Equals, true)
   235  		c.Check(len(runCmd) > 2, jc.IsTrue)
   236  	}
   237  }