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