func (s *CloudInitSuite) TestWindowsUserdataEncoding(c *gc.C) { series := "win8" metricsSpoolDir := must(paths.MetricsSpoolDir("win8")) toolsList := tools.List{ &tools.Tools{ URL: "http://foo.com/tools/released/juju1.2.3-win8-amd64.tgz", Version: version.MustParseBinary("1.2.3-win8-amd64"), Size: 10, SHA256: "1234", }, } dataDir, err := paths.DataDir(series) c.Assert(err, jc.ErrorIsNil) logDir, err := paths.LogDir(series) c.Assert(err, jc.ErrorIsNil) cfg := instancecfg.InstanceConfig{ ControllerTag: testing.ControllerTag, MachineId: "10", AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, Series: series, Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, MachineNonce: "FAKE_NONCE", APIInfo: &api.Info{ Addrs: []string{"state-addr.testing.invalid:54321"}, Password: "******", CACert: "CA CERT\n" + testing.CACert, Tag: names.NewMachineTag("10"), ModelTag: testing.ModelTag, }, MachineAgentServiceName: "jujud-machine-10", DataDir: dataDir, LogDir: path.Join(logDir, "juju"), MetricsSpoolDir: metricsSpoolDir, CloudInitOutputLog: path.Join(logDir, "cloud-init-output.log"), } err = cfg.SetTools(toolsList) c.Assert(err, jc.ErrorIsNil) ci, err := cloudinit.New("win8") c.Assert(err, jc.ErrorIsNil) udata, err := cloudconfig.NewUserdataConfig(&cfg, ci) c.Assert(err, jc.ErrorIsNil) err = udata.Configure() c.Assert(err, jc.ErrorIsNil) data, err := ci.RenderYAML() c.Assert(err, jc.ErrorIsNil) cicompose, err := cloudinit.New("win8") c.Assert(err, jc.ErrorIsNil) base64Data := base64.StdEncoding.EncodeToString(utils.Gzip(data)) got := []byte(fmt.Sprintf(cloudconfig.UserDataScript, base64Data)) expected, err := providerinit.ComposeUserData(&cfg, cicompose, openstack.OpenstackRenderer{}) c.Assert(err, jc.ErrorIsNil) c.Assert(string(got), gc.Equals, string(expected)) }
// NewInstanceConfig sets up a basic machine configuration, for a // non-bootstrap node. You'll still need to supply more information, // but this takes care of the fixed entries and the ones that are // always needed. func NewInstanceConfig( machineID, machineNonce, imageStream, series, publicImageSigningKey string, secureServerConnections bool, networks []string, mongoInfo *mongo.MongoInfo, apiInfo *api.Info, ) (*InstanceConfig, error) { dataDir, err := paths.DataDir(series) if err != nil { return nil, err } logDir, err := paths.LogDir(series) if err != nil { return nil, err } metricsSpoolDir, err := paths.MetricsSpoolDir(series) if err != nil { return nil, err } cloudInitOutputLog := path.Join(logDir, "cloud-init-output.log") icfg := &InstanceConfig{ // Fixed entries. DataDir: dataDir, LogDir: path.Join(logDir, "juju"), MetricsSpoolDir: metricsSpoolDir, Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, CloudInitOutputLog: cloudInitOutputLog, MachineAgentServiceName: "jujud-" + names.NewMachineTag(machineID).String(), Series: series, Tags: map[string]string{}, // Parameter entries. MachineId: machineID, MachineNonce: machineNonce, Networks: networks, MongoInfo: mongoInfo, APIInfo: apiInfo, ImageStream: imageStream, PublicImageSigningKey: publicImageSigningKey, AgentEnvironment: map[string]string{ agent.AllowsSecureConnection: strconv.FormatBool(secureServerConnections), }, } return icfg, nil }
// NewInstanceConfig sets up a basic machine configuration, for a // non-bootstrap node. You'll still need to supply more information, // but this takes care of the fixed entries and the ones that are // always needed. func NewInstanceConfig( controllerTag names.ControllerTag, machineID, machineNonce, imageStream, series string, apiInfo *api.Info, ) (*InstanceConfig, error) { dataDir, err := paths.DataDir(series) if err != nil { return nil, err } logDir, err := paths.LogDir(series) if err != nil { return nil, err } metricsSpoolDir, err := paths.MetricsSpoolDir(series) if err != nil { return nil, err } cloudInitOutputLog := path.Join(logDir, "cloud-init-output.log") icfg := &InstanceConfig{ // Fixed entries. DataDir: dataDir, LogDir: path.Join(logDir, "juju"), MetricsSpoolDir: metricsSpoolDir, Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, CloudInitOutputLog: cloudInitOutputLog, MachineAgentServiceName: "jujud-" + names.NewMachineTag(machineID).String(), Series: series, Tags: map[string]string{}, // Parameter entries. ControllerTag: controllerTag, MachineId: machineID, MachineNonce: machineNonce, APIInfo: apiInfo, ImageStream: imageStream, } return icfg, nil }
var logger = loggo.GetLogger("juju.agent") const ( // UninstallAgentFile is the name of the file inside the data // dir that, if it exists, will cause a machine agent to uninstall // when it receives the termination signal. UninstallAgentFile = "uninstall-agent" ) // These are base values used for the corresponding defaults. var ( logDir = paths.MustSucceed(paths.LogDir(series.HostSeries())) dataDir = paths.MustSucceed(paths.DataDir(series.HostSeries())) confDir = paths.MustSucceed(paths.ConfDir(series.HostSeries())) metricsSpoolDir = paths.MustSucceed(paths.MetricsSpoolDir(series.HostSeries())) ) // Agent exposes the agent's configuration to other components. This // interface should probably be segregated (agent.ConfigGetter and // agent.ConfigChanger?) but YAGNI *currently* advises against same. type Agent interface { // CurrentConfig returns a copy of the agent's configuration. No // guarantees regarding ongoing correctness are made. CurrentConfig() Config // ChangeConfig allows clients to change the agent's configuration // by supplying a callback that applies the changes. ChangeConfig(ConfigMutator) error }
type customDataSuite struct { testing.BaseSuite } var _ = gc.Suite(&customDataSuite{}) func must(s string, err error) string { if err != nil { panic(err) } return s } var logDir = must(paths.LogDir("precise")) var metricsSpoolDir = must(paths.MetricsSpoolDir("precise")) var dataDir = must(paths.DataDir("precise")) var cloudInitOutputLog = path.Join(logDir, "cloud-init-output.log") // makeInstanceConfig produces a valid cloudinit machine config. func makeInstanceConfig(c *gc.C) *instancecfg.InstanceConfig { machineId := "0" machineTag := names.NewMachineTag(machineId) return &instancecfg.InstanceConfig{ MachineId: machineId, MachineNonce: "gxshasqlnng", DataDir: dataDir, LogDir: logDir, MetricsSpoolDir: metricsSpoolDir, Jobs: []multiwatcher.MachineJob{ multiwatcher.JobManageEnviron,
func (*CloudInitSuite) testUserData(c *gc.C, series string, bootstrap bool) { testJujuHome := c.MkDir() defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome)) // Use actual series paths instead of local defaults logDir := must(paths.LogDir(series)) metricsSpoolDir := must(paths.MetricsSpoolDir(series)) dataDir := must(paths.DataDir(series)) tools := &tools.Tools{ URL: "http://tools.testing/tools/released/juju.tgz", Version: version.Binary{version.MustParse("1.2.3"), "quantal", "amd64"}, } envConfig, err := config.New(config.NoDefaults, dummySampleConfig()) c.Assert(err, jc.ErrorIsNil) allJobs := []multiwatcher.MachineJob{ multiwatcher.JobManageModel, multiwatcher.JobHostUnits, multiwatcher.JobManageNetworking, } cfg := &instancecfg.InstanceConfig{ MachineId: "10", MachineNonce: "5432", Tools: tools, Series: series, MongoInfo: &mongo.MongoInfo{ Info: mongo.Info{ Addrs: []string{"127.0.0.1:1234"}, CACert: "CA CERT\n" + testing.CACert, }, Password: "******", Tag: names.NewMachineTag("10"), }, APIInfo: &api.Info{ Addrs: []string{"127.0.0.1:1234"}, Password: "******", CACert: "CA CERT\n" + testing.CACert, Tag: names.NewMachineTag("10"), ModelTag: testing.ModelTag, }, DataDir: dataDir, LogDir: path.Join(logDir, "juju"), MetricsSpoolDir: metricsSpoolDir, Jobs: allJobs, CloudInitOutputLog: path.Join(logDir, "cloud-init-output.log"), Config: envConfig, AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, AuthorizedKeys: "wheredidileavemykeys", MachineAgentServiceName: "jujud-machine-10", EnableOSUpgrade: true, } if bootstrap { cfg.Bootstrap = true cfg.StateServingInfo = ¶ms.StateServingInfo{ StatePort: envConfig.StatePort(), APIPort: envConfig.APIPort(), Cert: testing.ServerCert, PrivateKey: testing.ServerKey, CAPrivateKey: testing.CAKey, } } script1 := "script1" script2 := "script2" cloudcfg, err := cloudinit.New(series) c.Assert(err, jc.ErrorIsNil) cloudcfg.AddRunCmd(script1) cloudcfg.AddRunCmd(script2) result, err := providerinit.ComposeUserData(cfg, cloudcfg, &openstack.OpenstackRenderer{}) c.Assert(err, jc.ErrorIsNil) unzipped, err := utils.Gunzip(result) c.Assert(err, jc.ErrorIsNil) config := make(map[interface{}]interface{}) err = goyaml.Unmarshal(unzipped, &config) c.Assert(err, jc.ErrorIsNil) // The scripts given to userData where added as the first // commands to be run. runCmd := config["runcmd"].([]interface{}) c.Check(runCmd[0], gc.Equals, script1) c.Check(runCmd[1], gc.Equals, script2) if bootstrap { // The cloudinit config should have nothing but the basics: // SSH authorized keys, the additional runcmds, and log output. // // Note: the additional runcmds *do* belong here, at least // for MAAS. MAAS needs to configure and then bounce the // network interfaces, which would sever the SSH connection // in the synchronous bootstrap phase. expected := map[interface{}]interface{}{ "output": map[interface{}]interface{}{ "all": "| tee -a /var/log/cloud-init-output.log", }, "runcmd": []interface{}{ "script1", "script2", "set -xe", "install -D -m 644 /dev/null '/etc/init/juju-clean-shutdown.conf'", "printf '%s\\n' '\nauthor \"Juju Team <*****@*****.**>\"\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'", "install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'", "printf '%s\\n' '5432' > '/var/lib/juju/nonce.txt'", }, } // Series with old cloudinit versions don't support adding // users so need the old way to set SSH authorized keys. if series == "precise" { expected["ssh_authorized_keys"] = []interface{}{ "wheredidileavemykeys", } } else { expected["users"] = []interface{}{ map[interface{}]interface{}{ "name": "ubuntu", "lock_passwd": true, "groups": []interface{}{"adm", "audio", "cdrom", "dialout", "dip", "floppy", "netdev", "plugdev", "sudo", "video"}, "shell": "/bin/bash", "sudo": []interface{}{"ALL=(ALL) NOPASSWD:ALL"}, "ssh-authorized-keys": []interface{}{"wheredidileavemykeys"}, }, } } c.Check(config, jc.DeepEquals, expected) } else { // Just check that the cloudinit config looks good, // and that there are more runcmds than the additional // ones we passed into ComposeUserData. c.Check(config["package_upgrade"], jc.IsTrue) c.Check(len(runCmd) > 2, jc.IsTrue) } }
func metricsSpoolDir(series string) string { return must(paths.MetricsSpoolDir(series)) }