github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/utils" 14 "github.com/juju/version" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/juju/names.v2" 17 goyaml "gopkg.in/yaml.v2" 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/cloudconfig" 23 "github.com/juju/juju/cloudconfig/cloudinit" 24 "github.com/juju/juju/cloudconfig/instancecfg" 25 "github.com/juju/juju/cloudconfig/providerinit" 26 "github.com/juju/juju/environs/config" 27 "github.com/juju/juju/juju/paths" 28 "github.com/juju/juju/mongo" 29 "github.com/juju/juju/provider/dummy" 30 "github.com/juju/juju/provider/openstack" 31 "github.com/juju/juju/state/multiwatcher" 32 "github.com/juju/juju/testing" 33 "github.com/juju/juju/tools" 34 ) 35 36 // dummySampleConfig returns the dummy sample config without 37 // the controller configured. 38 // This function also exists in environs/config_test 39 // Maybe place it in dummy and export it? 40 func dummySampleConfig() testing.Attrs { 41 return dummy.SampleConfig().Merge(testing.Attrs{ 42 "controller": false, 43 }) 44 } 45 46 type CloudInitSuite struct { 47 testing.FakeJujuXDGDataHomeSuite 48 } 49 50 var _ = gc.Suite(&CloudInitSuite{}) 51 52 // TODO: add this to the utils package 53 func must(s string, err error) string { 54 if err != nil { 55 panic(err) 56 } 57 return s 58 } 59 60 func (s *CloudInitSuite) TestFinishInstanceConfig(c *gc.C) { 61 62 userTag := names.NewLocalUserTag("not-touched") 63 64 expectedMcfg := &instancecfg.InstanceConfig{ 65 AuthorizedKeys: "we-are-the-keys", 66 AgentEnvironment: map[string]string{ 67 agent.ProviderType: "dummy", 68 agent.ContainerType: "", 69 }, 70 APIInfo: &api.Info{Tag: userTag}, 71 DisableSSLHostnameVerification: false, 72 EnableOSRefreshUpdate: true, 73 EnableOSUpgrade: true, 74 } 75 76 cfg, err := config.New(config.NoDefaults, dummySampleConfig().Merge(testing.Attrs{ 77 "authorized-keys": "we-are-the-keys", 78 })) 79 c.Assert(err, jc.ErrorIsNil) 80 81 icfg := &instancecfg.InstanceConfig{ 82 APIInfo: &api.Info{Tag: userTag}, 83 } 84 err = instancecfg.FinishInstanceConfig(icfg, cfg) 85 86 c.Assert(err, jc.ErrorIsNil) 87 c.Assert(icfg, jc.DeepEquals, expectedMcfg) 88 89 // Test when updates/upgrades are set to false. 90 cfg, err = config.New(config.NoDefaults, dummySampleConfig().Merge(testing.Attrs{ 91 "authorized-keys": "we-are-the-keys", 92 "enable-os-refresh-update": false, 93 "enable-os-upgrade": false, 94 })) 95 c.Assert(err, jc.ErrorIsNil) 96 err = instancecfg.FinishInstanceConfig(icfg, cfg) 97 c.Assert(err, jc.ErrorIsNil) 98 expectedMcfg.EnableOSRefreshUpdate = false 99 expectedMcfg.EnableOSUpgrade = false 100 c.Assert(icfg, jc.DeepEquals, expectedMcfg) 101 } 102 103 func (s *CloudInitSuite) TestFinishInstanceConfigNonDefault(c *gc.C) { 104 userTag := names.NewLocalUserTag("not-touched") 105 attrs := dummySampleConfig().Merge(testing.Attrs{ 106 "authorized-keys": "we-are-the-keys", 107 "ssl-hostname-verification": false, 108 }) 109 cfg, err := config.New(config.NoDefaults, attrs) 110 c.Assert(err, jc.ErrorIsNil) 111 icfg := &instancecfg.InstanceConfig{ 112 APIInfo: &api.Info{Tag: userTag}, 113 } 114 err = instancecfg.FinishInstanceConfig(icfg, cfg) 115 c.Assert(err, jc.ErrorIsNil) 116 c.Assert(icfg, jc.DeepEquals, &instancecfg.InstanceConfig{ 117 AuthorizedKeys: "we-are-the-keys", 118 AgentEnvironment: map[string]string{ 119 agent.ProviderType: "dummy", 120 agent.ContainerType: "", 121 }, 122 APIInfo: &api.Info{Tag: userTag}, 123 DisableSSLHostnameVerification: true, 124 EnableOSRefreshUpdate: true, 125 EnableOSUpgrade: true, 126 }) 127 } 128 129 func (s *CloudInitSuite) TestUserData(c *gc.C) { 130 s.testUserData(c, "quantal", false) 131 } 132 133 func (s *CloudInitSuite) TestControllerUserData(c *gc.C) { 134 s.testUserData(c, "quantal", true) 135 } 136 137 func (s *CloudInitSuite) TestControllerUserDataPrecise(c *gc.C) { 138 s.testUserData(c, "precise", true) 139 } 140 141 func (*CloudInitSuite) testUserData(c *gc.C, series string, bootstrap bool) { 142 // Use actual series paths instead of local defaults 143 logDir := must(paths.LogDir(series)) 144 metricsSpoolDir := must(paths.MetricsSpoolDir(series)) 145 dataDir := must(paths.DataDir(series)) 146 toolsList := tools.List{ 147 &tools.Tools{ 148 URL: "http://tools.testing/tools/released/juju.tgz", 149 Version: version.Binary{version.MustParse("1.2.3"), "quantal", "amd64"}, 150 }, 151 } 152 envConfig, err := config.New(config.NoDefaults, dummySampleConfig()) 153 c.Assert(err, jc.ErrorIsNil) 154 155 allJobs := []multiwatcher.MachineJob{ 156 multiwatcher.JobManageModel, 157 multiwatcher.JobHostUnits, 158 } 159 cfg := &instancecfg.InstanceConfig{ 160 ControllerTag: testing.ControllerTag, 161 MachineId: "10", 162 MachineNonce: "5432", 163 Series: series, 164 APIInfo: &api.Info{ 165 Addrs: []string{"127.0.0.1:1234"}, 166 Password: "pw2", 167 CACert: "CA CERT\n" + testing.CACert, 168 Tag: names.NewMachineTag("10"), 169 ModelTag: testing.ModelTag, 170 }, 171 DataDir: dataDir, 172 LogDir: path.Join(logDir, "juju"), 173 MetricsSpoolDir: metricsSpoolDir, 174 Jobs: allJobs, 175 CloudInitOutputLog: path.Join(logDir, "cloud-init-output.log"), 176 AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, 177 AuthorizedKeys: "wheredidileavemykeys", 178 MachineAgentServiceName: "jujud-machine-10", 179 EnableOSUpgrade: true, 180 } 181 err = cfg.SetTools(toolsList) 182 c.Assert(err, jc.ErrorIsNil) 183 if bootstrap { 184 controllerCfg := testing.FakeControllerConfig() 185 cfg.Bootstrap = &instancecfg.BootstrapConfig{ 186 StateInitializationParams: instancecfg.StateInitializationParams{ 187 ControllerConfig: controllerCfg, 188 ControllerModelConfig: envConfig, 189 }, 190 StateServingInfo: params.StateServingInfo{ 191 StatePort: controllerCfg.StatePort(), 192 APIPort: controllerCfg.APIPort(), 193 Cert: testing.ServerCert, 194 PrivateKey: testing.ServerKey, 195 CAPrivateKey: testing.CAKey, 196 }, 197 } 198 cfg.Controller = &instancecfg.ControllerConfig{ 199 MongoInfo: &mongo.MongoInfo{ 200 Info: mongo.Info{ 201 Addrs: []string{"127.0.0.1:1234"}, 202 CACert: "CA CERT\n" + testing.CACert, 203 }, 204 Password: "pw1", 205 Tag: names.NewMachineTag("10"), 206 }, 207 } 208 } 209 script1 := "script1" 210 script2 := "script2" 211 cloudcfg, err := cloudinit.New(series) 212 c.Assert(err, jc.ErrorIsNil) 213 cloudcfg.AddRunCmd(script1) 214 cloudcfg.AddRunCmd(script2) 215 result, err := providerinit.ComposeUserData(cfg, cloudcfg, &openstack.OpenstackRenderer{}) 216 c.Assert(err, jc.ErrorIsNil) 217 218 unzipped, err := utils.Gunzip(result) 219 c.Assert(err, jc.ErrorIsNil) 220 221 config := make(map[interface{}]interface{}) 222 err = goyaml.Unmarshal(unzipped, &config) 223 c.Assert(err, jc.ErrorIsNil) 224 225 // The scripts given to userData where added as the first 226 // commands to be run. 227 runCmd := config["runcmd"].([]interface{}) 228 c.Check(runCmd[0], gc.Equals, script1) 229 c.Check(runCmd[1], gc.Equals, script2) 230 231 if bootstrap { 232 // The cloudinit config should have nothing but the basics: 233 // SSH authorized keys, the additional runcmds, and log output. 234 // 235 // Note: the additional runcmds *do* belong here, at least 236 // for MAAS. MAAS needs to configure and then bounce the 237 // network interfaces, which would sever the SSH connection 238 // in the synchronous bootstrap phase. 239 expected := map[interface{}]interface{}{ 240 "output": map[interface{}]interface{}{ 241 "all": "| tee -a /var/log/cloud-init-output.log", 242 }, 243 "runcmd": []interface{}{ 244 "script1", "script2", 245 "set -xe", 246 "install -D -m 644 /dev/null '/etc/init/juju-clean-shutdown.conf'", 247 "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'", 248 "install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'", 249 "printf '%s\\n' '5432' > '/var/lib/juju/nonce.txt'", 250 }, 251 } 252 // Series with old cloudinit versions don't support adding 253 // users so need the old way to set SSH authorized keys. 254 if series == "precise" { 255 expected["ssh_authorized_keys"] = []interface{}{ 256 "wheredidileavemykeys", 257 } 258 } else { 259 expected["users"] = []interface{}{ 260 map[interface{}]interface{}{ 261 "name": "ubuntu", 262 "lock_passwd": true, 263 "groups": []interface{}{"adm", "audio", 264 "cdrom", "dialout", "dip", 265 "floppy", "netdev", "plugdev", 266 "sudo", "video"}, 267 "shell": "/bin/bash", 268 "sudo": []interface{}{"ALL=(ALL) NOPASSWD:ALL"}, 269 "ssh-authorized-keys": []interface{}{"wheredidileavemykeys"}, 270 }, 271 } 272 } 273 c.Check(config, jc.DeepEquals, expected) 274 } else { 275 // Just check that the cloudinit config looks good, 276 // and that there are more runcmds than the additional 277 // ones we passed into ComposeUserData. 278 c.Check(config["package_upgrade"], jc.IsTrue) 279 c.Check(len(runCmd) > 2, jc.IsTrue) 280 } 281 } 282 283 func (s *CloudInitSuite) TestWindowsUserdataEncoding(c *gc.C) { 284 series := "win8" 285 metricsSpoolDir := must(paths.MetricsSpoolDir("win8")) 286 toolsList := tools.List{ 287 &tools.Tools{ 288 URL: "http://foo.com/tools/released/juju1.2.3-win8-amd64.tgz", 289 Version: version.MustParseBinary("1.2.3-win8-amd64"), 290 Size: 10, 291 SHA256: "1234", 292 }, 293 } 294 dataDir, err := paths.DataDir(series) 295 c.Assert(err, jc.ErrorIsNil) 296 logDir, err := paths.LogDir(series) 297 c.Assert(err, jc.ErrorIsNil) 298 299 cfg := instancecfg.InstanceConfig{ 300 ControllerTag: testing.ControllerTag, 301 MachineId: "10", 302 AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, 303 Series: series, 304 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 305 MachineNonce: "FAKE_NONCE", 306 APIInfo: &api.Info{ 307 Addrs: []string{"state-addr.testing.invalid:54321"}, 308 Password: "bletch", 309 CACert: "CA CERT\n" + testing.CACert, 310 Tag: names.NewMachineTag("10"), 311 ModelTag: testing.ModelTag, 312 }, 313 MachineAgentServiceName: "jujud-machine-10", 314 DataDir: dataDir, 315 LogDir: path.Join(logDir, "juju"), 316 MetricsSpoolDir: metricsSpoolDir, 317 CloudInitOutputLog: path.Join(logDir, "cloud-init-output.log"), 318 } 319 err = cfg.SetTools(toolsList) 320 c.Assert(err, jc.ErrorIsNil) 321 322 ci, err := cloudinit.New("win8") 323 c.Assert(err, jc.ErrorIsNil) 324 325 udata, err := cloudconfig.NewUserdataConfig(&cfg, ci) 326 c.Assert(err, jc.ErrorIsNil) 327 328 err = udata.Configure() 329 c.Assert(err, jc.ErrorIsNil) 330 331 data, err := ci.RenderYAML() 332 c.Assert(err, jc.ErrorIsNil) 333 334 cicompose, err := cloudinit.New("win8") 335 c.Assert(err, jc.ErrorIsNil) 336 337 base64Data := base64.StdEncoding.EncodeToString(utils.Gzip(data)) 338 got := []byte(fmt.Sprintf(cloudconfig.UserDataScript, base64Data)) 339 expected, err := providerinit.ComposeUserData(&cfg, cicompose, openstack.OpenstackRenderer{}) 340 c.Assert(err, jc.ErrorIsNil) 341 c.Assert(string(got), gc.Equals, string(expected)) 342 }