github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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 "path" 8 "time" 9 10 "github.com/juju/names" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils" 13 gc "gopkg.in/check.v1" 14 goyaml "gopkg.in/yaml.v1" 15 16 "github.com/juju/juju/agent" 17 "github.com/juju/juju/api" 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/cert" 20 coreCloudinit "github.com/juju/juju/cloudinit" 21 "github.com/juju/juju/environs" 22 "github.com/juju/juju/environs/cloudinit" 23 "github.com/juju/juju/environs/config" 24 "github.com/juju/juju/juju/osenv" 25 "github.com/juju/juju/juju/paths" 26 "github.com/juju/juju/mongo" 27 "github.com/juju/juju/provider/dummy" 28 "github.com/juju/juju/state/multiwatcher" 29 "github.com/juju/juju/testing" 30 "github.com/juju/juju/tools" 31 "github.com/juju/juju/version" 32 ) 33 34 // dummySampleConfig returns the dummy sample config without 35 // the state server configured. 36 // will not run a state server. 37 func dummySampleConfig() testing.Attrs { 38 return dummy.SampleConfig().Merge(testing.Attrs{ 39 "state-server": false, 40 }) 41 } 42 43 type CloudInitSuite struct { 44 testing.BaseSuite 45 } 46 47 var _ = gc.Suite(&CloudInitSuite{}) 48 49 func must(s string, err error) string { 50 if err != nil { 51 panic(err) 52 } 53 return s 54 } 55 56 var logDir = must(paths.LogDir("precise")) 57 var cloudInitOutputLog = path.Join(logDir, "cloud-init-output.log") 58 59 func (s *CloudInitSuite) TestFinishInstanceConfig(c *gc.C) { 60 61 userTag := names.NewLocalUserTag("not-touched") 62 63 expectedMcfg := &cloudinit.MachineConfig{ 64 AuthorizedKeys: "we-are-the-keys", 65 AgentEnvironment: map[string]string{ 66 agent.ProviderType: "dummy", 67 agent.ContainerType: "", 68 }, 69 MongoInfo: &mongo.MongoInfo{Tag: userTag}, 70 APIInfo: &api.Info{Tag: userTag}, 71 DisableSSLHostnameVerification: false, 72 PreferIPv6: true, 73 EnableOSRefreshUpdate: true, 74 EnableOSUpgrade: true, 75 } 76 77 cfg, err := config.New(config.NoDefaults, dummySampleConfig().Merge(testing.Attrs{ 78 "authorized-keys": "we-are-the-keys", 79 })) 80 c.Assert(err, jc.ErrorIsNil) 81 82 mcfg := &cloudinit.MachineConfig{ 83 MongoInfo: &mongo.MongoInfo{Tag: userTag}, 84 APIInfo: &api.Info{Tag: userTag}, 85 } 86 err = environs.FinishMachineConfig(mcfg, cfg) 87 88 c.Assert(err, jc.ErrorIsNil) 89 c.Assert(mcfg, jc.DeepEquals, expectedMcfg) 90 91 // Test when updates/upgrades are set to false. 92 cfg, err = config.New(config.NoDefaults, dummySampleConfig().Merge(testing.Attrs{ 93 "authorized-keys": "we-are-the-keys", 94 "enable-os-refresh-update": false, 95 "enable-os-upgrade": false, 96 })) 97 c.Assert(err, jc.ErrorIsNil) 98 err = environs.FinishMachineConfig(mcfg, cfg) 99 c.Assert(err, jc.ErrorIsNil) 100 expectedMcfg.EnableOSRefreshUpdate = false 101 expectedMcfg.EnableOSUpgrade = false 102 c.Assert(mcfg, jc.DeepEquals, expectedMcfg) 103 } 104 105 func (s *CloudInitSuite) TestFinishMachineConfigNonDefault(c *gc.C) { 106 userTag := names.NewLocalUserTag("not-touched") 107 attrs := dummySampleConfig().Merge(testing.Attrs{ 108 "authorized-keys": "we-are-the-keys", 109 "ssl-hostname-verification": false, 110 }) 111 cfg, err := config.New(config.NoDefaults, attrs) 112 c.Assert(err, jc.ErrorIsNil) 113 mcfg := &cloudinit.MachineConfig{ 114 MongoInfo: &mongo.MongoInfo{Tag: userTag}, 115 APIInfo: &api.Info{Tag: userTag}, 116 } 117 err = environs.FinishMachineConfig(mcfg, cfg) 118 c.Assert(err, jc.ErrorIsNil) 119 c.Assert(mcfg, jc.DeepEquals, &cloudinit.MachineConfig{ 120 AuthorizedKeys: "we-are-the-keys", 121 AgentEnvironment: map[string]string{ 122 agent.ProviderType: "dummy", 123 agent.ContainerType: "", 124 }, 125 MongoInfo: &mongo.MongoInfo{Tag: userTag}, 126 APIInfo: &api.Info{Tag: userTag}, 127 DisableSSLHostnameVerification: true, 128 PreferIPv6: true, 129 EnableOSRefreshUpdate: true, 130 EnableOSUpgrade: true, 131 }) 132 } 133 134 func (s *CloudInitSuite) TestFinishBootstrapConfig(c *gc.C) { 135 attrs := dummySampleConfig().Merge(testing.Attrs{ 136 "authorized-keys": "we-are-the-keys", 137 "admin-secret": "lisboan-pork", 138 "agent-version": "1.2.3", 139 "state-server": false, 140 }) 141 cfg, err := config.New(config.NoDefaults, attrs) 142 c.Assert(err, jc.ErrorIsNil) 143 oldAttrs := cfg.AllAttrs() 144 mcfg := &cloudinit.MachineConfig{ 145 Bootstrap: true, 146 } 147 err = environs.FinishMachineConfig(mcfg, cfg) 148 c.Assert(err, jc.ErrorIsNil) 149 c.Check(mcfg.AuthorizedKeys, gc.Equals, "we-are-the-keys") 150 c.Check(mcfg.DisableSSLHostnameVerification, jc.IsFalse) 151 password := utils.UserPasswordHash("lisboan-pork", utils.CompatSalt) 152 c.Check(mcfg.APIInfo, gc.DeepEquals, &api.Info{ 153 Password: password, CACert: testing.CACert, 154 EnvironTag: testing.EnvironmentTag, 155 }) 156 c.Check(mcfg.MongoInfo, gc.DeepEquals, &mongo.MongoInfo{ 157 Password: password, Info: mongo.Info{CACert: testing.CACert}, 158 }) 159 c.Check(mcfg.StateServingInfo.StatePort, gc.Equals, cfg.StatePort()) 160 c.Check(mcfg.StateServingInfo.APIPort, gc.Equals, cfg.APIPort()) 161 c.Check(mcfg.StateServingInfo.CAPrivateKey, gc.Equals, oldAttrs["ca-private-key"]) 162 163 oldAttrs["ca-private-key"] = "" 164 oldAttrs["admin-secret"] = "" 165 c.Check(mcfg.Config.AllAttrs(), gc.DeepEquals, oldAttrs) 166 srvCertPEM := mcfg.StateServingInfo.Cert 167 srvKeyPEM := mcfg.StateServingInfo.PrivateKey 168 _, _, err = cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) 169 c.Check(err, jc.ErrorIsNil) 170 171 err = cert.Verify(srvCertPEM, testing.CACert, time.Now()) 172 c.Assert(err, jc.ErrorIsNil) 173 err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(9, 0, 0)) 174 c.Assert(err, jc.ErrorIsNil) 175 err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(10, 0, 1)) 176 c.Assert(err, gc.NotNil) 177 } 178 179 func (s *CloudInitSuite) TestUserData(c *gc.C) { 180 s.testUserData(c, false) 181 } 182 183 func (s *CloudInitSuite) TestStateServerUserData(c *gc.C) { 184 s.testUserData(c, true) 185 } 186 187 func (*CloudInitSuite) testUserData(c *gc.C, bootstrap bool) { 188 testJujuHome := c.MkDir() 189 defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome)) 190 // Use actual series paths instead of local defaults 191 logDir := must(paths.LogDir("quantal")) 192 dataDir := must(paths.DataDir("quantal")) 193 tools := &tools.Tools{ 194 URL: "http://foo.com/tools/released/juju1.2.3-quantal-amd64.tgz", 195 Version: version.MustParseBinary("1.2.3-quantal-amd64"), 196 } 197 envConfig, err := config.New(config.NoDefaults, dummySampleConfig()) 198 c.Assert(err, jc.ErrorIsNil) 199 200 allJobs := []multiwatcher.MachineJob{ 201 multiwatcher.JobManageEnviron, 202 multiwatcher.JobHostUnits, 203 multiwatcher.JobManageNetworking, 204 } 205 cfg := &cloudinit.MachineConfig{ 206 MachineId: "10", 207 MachineNonce: "5432", 208 Tools: tools, 209 Series: "quantal", 210 MongoInfo: &mongo.MongoInfo{ 211 Info: mongo.Info{ 212 Addrs: []string{"127.0.0.1:1234"}, 213 CACert: "CA CERT\n" + testing.CACert, 214 }, 215 Password: "pw1", 216 Tag: names.NewMachineTag("10"), 217 }, 218 APIInfo: &api.Info{ 219 Addrs: []string{"127.0.0.1:1234"}, 220 Password: "pw2", 221 CACert: "CA CERT\n" + testing.CACert, 222 Tag: names.NewMachineTag("10"), 223 EnvironTag: testing.EnvironmentTag, 224 }, 225 DataDir: dataDir, 226 LogDir: path.Join(logDir, "juju"), 227 Jobs: allJobs, 228 CloudInitOutputLog: cloudInitOutputLog, 229 Config: envConfig, 230 AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, 231 AuthorizedKeys: "wheredidileavemykeys", 232 MachineAgentServiceName: "jujud-machine-10", 233 EnableOSUpgrade: true, 234 } 235 if bootstrap { 236 cfg.Bootstrap = true 237 cfg.StateServingInfo = ¶ms.StateServingInfo{ 238 StatePort: envConfig.StatePort(), 239 APIPort: envConfig.APIPort(), 240 Cert: testing.ServerCert, 241 PrivateKey: testing.ServerKey, 242 CAPrivateKey: testing.CAKey, 243 } 244 } 245 script1 := "script1" 246 script2 := "script2" 247 cloudcfg := coreCloudinit.New() 248 cloudcfg.AddRunCmd(script1) 249 cloudcfg.AddRunCmd(script2) 250 result, err := environs.ComposeUserData(cfg, cloudcfg) 251 c.Assert(err, jc.ErrorIsNil) 252 253 unzipped, err := utils.Gunzip(result) 254 c.Assert(err, jc.ErrorIsNil) 255 256 config := make(map[interface{}]interface{}) 257 err = goyaml.Unmarshal(unzipped, &config) 258 c.Assert(err, jc.ErrorIsNil) 259 260 // The scripts given to userData where added as the first 261 // commands to be run. 262 runCmd := config["runcmd"].([]interface{}) 263 c.Check(runCmd[0], gc.Equals, script1) 264 c.Check(runCmd[1], gc.Equals, script2) 265 266 if bootstrap { 267 // The cloudinit config should have nothing but the basics: 268 // SSH authorized keys, the additional runcmds, and log output. 269 // 270 // Note: the additional runcmds *do* belong here, at least 271 // for MAAS. MAAS needs to configure and then bounce the 272 // network interfaces, which would sever the SSH connection 273 // in the synchronous bootstrap phase. 274 c.Check(config, gc.DeepEquals, map[interface{}]interface{}{ 275 "output": map[interface{}]interface{}{ 276 "all": "| tee -a /var/log/cloud-init-output.log", 277 }, 278 "runcmd": []interface{}{ 279 "script1", "script2", 280 "set -xe", 281 "install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'", 282 "printf '%s\\n' '5432' > '/var/lib/juju/nonce.txt'", 283 }, 284 "ssh_authorized_keys": []interface{}{"wheredidileavemykeys"}, 285 }) 286 } else { 287 // Just check that the cloudinit config looks good, 288 // and that there are more runcmds than the additional 289 // ones we passed into ComposeUserData. 290 c.Check(config["apt_upgrade"], jc.IsTrue) 291 c.Check(len(runCmd) > 2, jc.IsTrue) 292 } 293 }