github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cloudconfig/providerinit/providerinit_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Copyright 2015 Cloudbase Solutions SRL 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 package providerinit_test 6 7 import ( 8 "encoding/base64" 9 "fmt" 10 "path" 11 "time" 12 13 "github.com/juju/names" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils" 16 gc "gopkg.in/check.v1" 17 goyaml "gopkg.in/yaml.v1" 18 19 "github.com/juju/juju/agent" 20 "github.com/juju/juju/api" 21 "github.com/juju/juju/apiserver/params" 22 "github.com/juju/juju/cert" 23 "github.com/juju/juju/cloudconfig" 24 "github.com/juju/juju/cloudconfig/cloudinit" 25 "github.com/juju/juju/cloudconfig/instancecfg" 26 "github.com/juju/juju/cloudconfig/providerinit" 27 "github.com/juju/juju/environs/config" 28 "github.com/juju/juju/juju/osenv" 29 "github.com/juju/juju/juju/paths" 30 "github.com/juju/juju/mongo" 31 "github.com/juju/juju/provider/dummy" 32 "github.com/juju/juju/provider/openstack" 33 "github.com/juju/juju/state/multiwatcher" 34 "github.com/juju/juju/testing" 35 "github.com/juju/juju/tools" 36 "github.com/juju/juju/version" 37 ) 38 39 // dummySampleConfig returns the dummy sample config without 40 // the state server configured. 41 // This function also exists in environs/config_test 42 // Maybe place it in dummy and export it? 43 func dummySampleConfig() testing.Attrs { 44 return dummy.SampleConfig().Merge(testing.Attrs{ 45 "state-server": false, 46 }) 47 } 48 49 type CloudInitSuite struct { 50 testing.BaseSuite 51 } 52 53 var _ = gc.Suite(&CloudInitSuite{}) 54 55 // TODO: add this to the utils package 56 func must(s string, err error) string { 57 if err != nil { 58 panic(err) 59 } 60 return s 61 } 62 63 func (s *CloudInitSuite) TestFinishInstanceConfig(c *gc.C) { 64 65 userTag := names.NewLocalUserTag("not-touched") 66 67 expectedMcfg := &instancecfg.InstanceConfig{ 68 AuthorizedKeys: "we-are-the-keys", 69 AgentEnvironment: map[string]string{ 70 agent.ProviderType: "dummy", 71 agent.ContainerType: "", 72 }, 73 MongoInfo: &mongo.MongoInfo{Tag: userTag}, 74 APIInfo: &api.Info{Tag: userTag}, 75 DisableSSLHostnameVerification: false, 76 PreferIPv6: true, 77 EnableOSRefreshUpdate: true, 78 EnableOSUpgrade: true, 79 } 80 81 cfg, err := config.New(config.NoDefaults, dummySampleConfig().Merge(testing.Attrs{ 82 "authorized-keys": "we-are-the-keys", 83 })) 84 c.Assert(err, jc.ErrorIsNil) 85 86 icfg := &instancecfg.InstanceConfig{ 87 MongoInfo: &mongo.MongoInfo{Tag: userTag}, 88 APIInfo: &api.Info{Tag: userTag}, 89 } 90 err = instancecfg.FinishInstanceConfig(icfg, cfg) 91 92 c.Assert(err, jc.ErrorIsNil) 93 c.Assert(icfg, jc.DeepEquals, expectedMcfg) 94 95 // Test when updates/upgrades are set to false. 96 cfg, err = config.New(config.NoDefaults, dummySampleConfig().Merge(testing.Attrs{ 97 "authorized-keys": "we-are-the-keys", 98 "enable-os-refresh-update": false, 99 "enable-os-upgrade": false, 100 })) 101 c.Assert(err, jc.ErrorIsNil) 102 err = instancecfg.FinishInstanceConfig(icfg, cfg) 103 c.Assert(err, jc.ErrorIsNil) 104 expectedMcfg.EnableOSRefreshUpdate = false 105 expectedMcfg.EnableOSUpgrade = false 106 c.Assert(icfg, jc.DeepEquals, expectedMcfg) 107 } 108 109 func (s *CloudInitSuite) TestFinishInstanceConfigNonDefault(c *gc.C) { 110 userTag := names.NewLocalUserTag("not-touched") 111 attrs := dummySampleConfig().Merge(testing.Attrs{ 112 "authorized-keys": "we-are-the-keys", 113 "ssl-hostname-verification": false, 114 }) 115 cfg, err := config.New(config.NoDefaults, attrs) 116 c.Assert(err, jc.ErrorIsNil) 117 icfg := &instancecfg.InstanceConfig{ 118 MongoInfo: &mongo.MongoInfo{Tag: userTag}, 119 APIInfo: &api.Info{Tag: userTag}, 120 } 121 err = instancecfg.FinishInstanceConfig(icfg, cfg) 122 c.Assert(err, jc.ErrorIsNil) 123 c.Assert(icfg, jc.DeepEquals, &instancecfg.InstanceConfig{ 124 AuthorizedKeys: "we-are-the-keys", 125 AgentEnvironment: map[string]string{ 126 agent.ProviderType: "dummy", 127 agent.ContainerType: "", 128 }, 129 MongoInfo: &mongo.MongoInfo{Tag: userTag}, 130 APIInfo: &api.Info{Tag: userTag}, 131 DisableSSLHostnameVerification: true, 132 PreferIPv6: true, 133 EnableOSRefreshUpdate: true, 134 EnableOSUpgrade: true, 135 }) 136 } 137 138 func (s *CloudInitSuite) TestFinishBootstrapConfig(c *gc.C) { 139 attrs := dummySampleConfig().Merge(testing.Attrs{ 140 "authorized-keys": "we-are-the-keys", 141 "admin-secret": "lisboan-pork", 142 "agent-version": "1.2.3", 143 "state-server": false, 144 }) 145 cfg, err := config.New(config.NoDefaults, attrs) 146 c.Assert(err, jc.ErrorIsNil) 147 oldAttrs := cfg.AllAttrs() 148 icfg := &instancecfg.InstanceConfig{ 149 Bootstrap: true, 150 } 151 err = instancecfg.FinishInstanceConfig(icfg, cfg) 152 c.Assert(err, jc.ErrorIsNil) 153 c.Check(icfg.AuthorizedKeys, gc.Equals, "we-are-the-keys") 154 c.Check(icfg.DisableSSLHostnameVerification, jc.IsFalse) 155 password := utils.UserPasswordHash("lisboan-pork", utils.CompatSalt) 156 c.Check(icfg.APIInfo, gc.DeepEquals, &api.Info{ 157 Password: password, CACert: testing.CACert, 158 EnvironTag: testing.EnvironmentTag, 159 }) 160 c.Check(icfg.MongoInfo, gc.DeepEquals, &mongo.MongoInfo{ 161 Password: password, Info: mongo.Info{CACert: testing.CACert}, 162 }) 163 c.Check(icfg.StateServingInfo.StatePort, gc.Equals, cfg.StatePort()) 164 c.Check(icfg.StateServingInfo.APIPort, gc.Equals, cfg.APIPort()) 165 c.Check(icfg.StateServingInfo.CAPrivateKey, gc.Equals, oldAttrs["ca-private-key"]) 166 167 oldAttrs["ca-private-key"] = "" 168 oldAttrs["admin-secret"] = "" 169 c.Check(icfg.Config.AllAttrs(), gc.DeepEquals, oldAttrs) 170 srvCertPEM := icfg.StateServingInfo.Cert 171 srvKeyPEM := icfg.StateServingInfo.PrivateKey 172 _, _, err = cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) 173 c.Check(err, jc.ErrorIsNil) 174 175 err = cert.Verify(srvCertPEM, testing.CACert, time.Now()) 176 c.Assert(err, jc.ErrorIsNil) 177 err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(9, 0, 0)) 178 c.Assert(err, jc.ErrorIsNil) 179 err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(10, 0, 1)) 180 c.Assert(err, gc.NotNil) 181 } 182 183 func (s *CloudInitSuite) TestUserData(c *gc.C) { 184 s.testUserData(c, false) 185 } 186 187 func (s *CloudInitSuite) TestStateServerUserData(c *gc.C) { 188 s.testUserData(c, true) 189 } 190 191 func (*CloudInitSuite) testUserData(c *gc.C, bootstrap bool) { 192 testJujuHome := c.MkDir() 193 defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome)) 194 // Use actual series paths instead of local defaults 195 logDir := must(paths.LogDir("quantal")) 196 metricsSpoolDir := must(paths.MetricsSpoolDir("quantal")) 197 dataDir := must(paths.DataDir("quantal")) 198 tools := &tools.Tools{ 199 URL: "http://foo.com/tools/released/juju1.2.3-quantal-amd64.tgz", 200 Version: version.MustParseBinary("1.2.3-quantal-amd64"), 201 } 202 envConfig, err := config.New(config.NoDefaults, dummySampleConfig()) 203 c.Assert(err, jc.ErrorIsNil) 204 205 series := "quantal" 206 allJobs := []multiwatcher.MachineJob{ 207 multiwatcher.JobManageEnviron, 208 multiwatcher.JobHostUnits, 209 multiwatcher.JobManageNetworking, 210 } 211 cfg := &instancecfg.InstanceConfig{ 212 MachineId: "10", 213 MachineNonce: "5432", 214 Tools: tools, 215 Series: series, 216 MongoInfo: &mongo.MongoInfo{ 217 Info: mongo.Info{ 218 Addrs: []string{"127.0.0.1:1234"}, 219 CACert: "CA CERT\n" + testing.CACert, 220 }, 221 Password: "pw1", 222 Tag: names.NewMachineTag("10"), 223 }, 224 APIInfo: &api.Info{ 225 Addrs: []string{"127.0.0.1:1234"}, 226 Password: "pw2", 227 CACert: "CA CERT\n" + testing.CACert, 228 Tag: names.NewMachineTag("10"), 229 EnvironTag: testing.EnvironmentTag, 230 }, 231 DataDir: dataDir, 232 LogDir: path.Join(logDir, "juju"), 233 MetricsSpoolDir: metricsSpoolDir, 234 Jobs: allJobs, 235 CloudInitOutputLog: path.Join(logDir, "cloud-init-output.log"), 236 Config: envConfig, 237 AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, 238 AuthorizedKeys: "wheredidileavemykeys", 239 MachineAgentServiceName: "jujud-machine-10", 240 EnableOSUpgrade: true, 241 } 242 if bootstrap { 243 cfg.Bootstrap = true 244 cfg.StateServingInfo = ¶ms.StateServingInfo{ 245 StatePort: envConfig.StatePort(), 246 APIPort: envConfig.APIPort(), 247 Cert: testing.ServerCert, 248 PrivateKey: testing.ServerKey, 249 CAPrivateKey: testing.CAKey, 250 } 251 } 252 script1 := "script1" 253 script2 := "script2" 254 cloudcfg, err := cloudinit.New(series) 255 c.Assert(err, jc.ErrorIsNil) 256 cloudcfg.AddRunCmd(script1) 257 cloudcfg.AddRunCmd(script2) 258 result, err := providerinit.ComposeUserData(cfg, cloudcfg, &openstack.OpenstackRenderer{}) 259 c.Assert(err, jc.ErrorIsNil) 260 261 unzipped, err := utils.Gunzip(result) 262 c.Assert(err, jc.ErrorIsNil) 263 264 config := make(map[interface{}]interface{}) 265 err = goyaml.Unmarshal(unzipped, &config) 266 c.Assert(err, jc.ErrorIsNil) 267 268 // The scripts given to userData where added as the first 269 // commands to be run. 270 runCmd := config["runcmd"].([]interface{}) 271 c.Check(runCmd[0], gc.Equals, script1) 272 c.Check(runCmd[1], gc.Equals, script2) 273 274 if bootstrap { 275 // The cloudinit config should have nothing but the basics: 276 // SSH authorized keys, the additional runcmds, and log output. 277 // 278 // Note: the additional runcmds *do* belong here, at least 279 // for MAAS. MAAS needs to configure and then bounce the 280 // network interfaces, which would sever the SSH connection 281 // in the synchronous bootstrap phase. 282 c.Check(config, jc.DeepEquals, map[interface{}]interface{}{ 283 "output": map[interface{}]interface{}{ 284 "all": "| tee -a /var/log/cloud-init-output.log", 285 }, 286 "runcmd": []interface{}{ 287 "script1", "script2", 288 "set -xe", 289 "install -D -m 644 /dev/null '/etc/init/juju-clean-shutdown.conf'", 290 "printf '%s\\n' '\nauthor \"Juju Team <juju@lists.ubuntu.com>\"\ndescription \"Stop all network interfaces on shutdown\"\nstart on runlevel [016]\ntask\nconsole output\n\nexec /sbin/ifdown -a -v --force\n' > '/etc/init/juju-clean-shutdown.conf'", 291 "install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'", 292 "printf '%s\\n' '5432' > '/var/lib/juju/nonce.txt'", 293 }, 294 "ssh_authorized_keys": []interface{}{"wheredidileavemykeys"}, 295 }) 296 } else { 297 // Just check that the cloudinit config looks good, 298 // and that there are more runcmds than the additional 299 // ones we passed into ComposeUserData. 300 c.Check(config["package_upgrade"], jc.IsTrue) 301 c.Check(len(runCmd) > 2, jc.IsTrue) 302 } 303 } 304 305 func (s *CloudInitSuite) TestWindowsUserdataEncoding(c *gc.C) { 306 series := "win8" 307 metricsSpoolDir := must(paths.MetricsSpoolDir("win8")) 308 tools := &tools.Tools{ 309 URL: "http://foo.com/tools/released/juju1.2.3-win8-amd64.tgz", 310 Version: version.MustParseBinary("1.2.3-win8-amd64"), 311 Size: 10, 312 SHA256: "1234", 313 } 314 dataDir, err := paths.DataDir(series) 315 c.Assert(err, jc.ErrorIsNil) 316 logDir, err := paths.LogDir(series) 317 c.Assert(err, jc.ErrorIsNil) 318 319 cfg := instancecfg.InstanceConfig{ 320 MachineId: "10", 321 AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, 322 Tools: tools, 323 Series: series, 324 Bootstrap: false, 325 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 326 MachineNonce: "FAKE_NONCE", 327 MongoInfo: &mongo.MongoInfo{ 328 Tag: names.NewMachineTag("10"), 329 Password: "arble", 330 Info: mongo.Info{ 331 CACert: "CA CERT\n" + testing.CACert, 332 Addrs: []string{"state-addr.testing.invalid:12345"}, 333 }, 334 }, 335 APIInfo: &api.Info{ 336 Addrs: []string{"state-addr.testing.invalid:54321"}, 337 Password: "bletch", 338 CACert: "CA CERT\n" + testing.CACert, 339 Tag: names.NewMachineTag("10"), 340 EnvironTag: testing.EnvironmentTag, 341 }, 342 MachineAgentServiceName: "jujud-machine-10", 343 DataDir: dataDir, 344 LogDir: path.Join(logDir, "juju"), 345 MetricsSpoolDir: metricsSpoolDir, 346 CloudInitOutputLog: path.Join(logDir, "cloud-init-output.log"), 347 } 348 349 ci, err := cloudinit.New("win8") 350 c.Assert(err, jc.ErrorIsNil) 351 352 udata, err := cloudconfig.NewUserdataConfig(&cfg, ci) 353 c.Assert(err, jc.ErrorIsNil) 354 err = udata.Configure() 355 c.Assert(err, jc.ErrorIsNil) 356 data, err := ci.RenderYAML() 357 c.Assert(err, jc.ErrorIsNil) 358 base64Data := base64.StdEncoding.EncodeToString(utils.Gzip(data)) 359 got := []byte(fmt.Sprintf(cloudconfig.UserdataScript, base64Data)) 360 361 cicompose, err := cloudinit.New("win8") 362 c.Assert(err, jc.ErrorIsNil) 363 expected, err := providerinit.ComposeUserData(&cfg, cicompose, openstack.OpenstackRenderer{}) 364 c.Assert(err, jc.ErrorIsNil) 365 366 c.Assert(string(expected), gc.Equals, string(got)) 367 }