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