func (s *bootstrapSuite) TestInitializeStateFailsSecondTime(c *gc.C) { dataDir := c.MkDir() pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt) configParams := agent.AgentConfigParams{ DataDir: dataDir, Tag: "machine-0", UpgradedToVersion: version.Current.Number, StateAddresses: []string{gitjujutesting.MgoServer.Addr()}, CACert: testing.CACert, Password: pwHash, } cfg, err := agent.NewAgentConfig(configParams) c.Assert(err, gc.IsNil) cfg.SetStateServingInfo(params.StateServingInfo{ APIPort: 5555, StatePort: gitjujutesting.MgoServer.Port(), Cert: "foo", PrivateKey: "bar", SharedSecret: "baz", SystemIdentity: "qux", }) expectConstraints := constraints.MustParse("mem=1024M") expectHW := instance.MustParseHardware("mem=2048M") mcfg := agent.BootstrapMachineConfig{ Constraints: expectConstraints, Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: "i-bootstrap", Characteristics: expectHW, } envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{ "agent-version": version.Current.Number.String(), "state-id": "1", // needed so policy can Open config }) envCfg, err := config.New(config.NoDefaults, envAttrs) c.Assert(err, gc.IsNil) st, _, err := agent.InitializeState(cfg, envCfg, mcfg, mongo.DialOpts{}, environs.NewStatePolicy()) c.Assert(err, gc.IsNil) err = st.SetAdminMongoPassword("") c.Check(err, gc.IsNil) st.Close() st, _, err = agent.InitializeState(cfg, envCfg, mcfg, mongo.DialOpts{}, environs.NewStatePolicy()) if err == nil { st.Close() } c.Assert(err, gc.ErrorMatches, "failed to initialize state: cannot create log collection: unauthorized mongo access: unauthorized") }
func (s *bootstrapSuite) TestInitializeStateFailsSecondTime(c *gc.C) { dataDir := c.MkDir() pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt) configParams := agent.AgentConfigParams{ Paths: agent.Paths{DataDir: dataDir}, Tag: names.NewMachineTag("0"), UpgradedToVersion: version.Current.Number, StateAddresses: []string{s.mgoInst.Addr()}, CACert: testing.CACert, Password: pwHash, Environment: testing.EnvironmentTag, } cfg, err := agent.NewAgentConfig(configParams) c.Assert(err, jc.ErrorIsNil) cfg.SetStateServingInfo(params.StateServingInfo{ APIPort: 5555, StatePort: s.mgoInst.Port(), Cert: "foo", PrivateKey: "bar", SharedSecret: "baz", SystemIdentity: "qux", }) expectConstraints := constraints.MustParse("mem=1024M") expectHW := instance.MustParseHardware("mem=2048M") mcfg := agent.BootstrapMachineConfig{ Constraints: expectConstraints, Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageEnviron}, InstanceId: "i-bootstrap", Characteristics: expectHW, } envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{ "agent-version": version.Current.Number.String(), "state-id": "1", // needed so policy can Open config }) envCfg, err := config.New(config.NoDefaults, envAttrs) c.Assert(err, jc.ErrorIsNil) adminUser := names.NewLocalUserTag("agent-admin") st, _, err := agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, jc.ErrorIsNil) st.Close() st, _, err = agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy()) if err == nil { st.Close() } c.Assert(err, gc.ErrorMatches, "failed to initialize mongo admin user: cannot set admin password: not authorized .*") }
func (s *bootstrapSuite) TestInitializeStateWithStateServingInfoNotAvailable(c *gc.C) { configParams := agent.AgentConfigParams{ DataDir: c.MkDir(), Tag: names.NewMachineTag("0"), UpgradedToVersion: version.Current.Number, StateAddresses: []string{s.mgoInst.Addr()}, CACert: testing.CACert, Password: "******", } cfg, err := agent.NewAgentConfig(configParams) c.Assert(err, gc.IsNil) _, available := cfg.StateServingInfo() c.Assert(available, gc.Equals, false) _, _, err = agent.InitializeState(cfg, nil, agent.BootstrapMachineConfig{}, mongo.DialOpts{}, environs.NewStatePolicy()) // InitializeState will fail attempting to get the api port information c.Assert(err, gc.ErrorMatches, "state serving information not available") }
func (s *bootstrapSuite) TestInitializeState(c *gc.C) { dataDir := c.MkDir() pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt) configParams := agent.AgentConfigParams{ DataDir: dataDir, Tag: names.NewMachineTag("0"), UpgradedToVersion: version.Current.Number, StateAddresses: []string{s.mgoInst.Addr()}, CACert: testing.CACert, Password: pwHash, } servingInfo := params.StateServingInfo{ Cert: testing.ServerCert, PrivateKey: testing.ServerKey, APIPort: 1234, StatePort: s.mgoInst.Port(), SystemIdentity: "def456", } cfg, err := agent.NewStateMachineConfig(configParams, servingInfo) c.Assert(err, gc.IsNil) _, available := cfg.StateServingInfo() c.Assert(available, gc.Equals, true) expectConstraints := constraints.MustParse("mem=1024M") expectHW := instance.MustParseHardware("mem=2048M") mcfg := agent.BootstrapMachineConfig{ Addresses: network.NewAddresses("zeroonetwothree", "0.1.2.3"), Constraints: expectConstraints, Jobs: []params.MachineJob{params.JobManageEnviron}, InstanceId: "i-bootstrap", Characteristics: expectHW, SharedSecret: "abc123", } envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{ "agent-version": version.Current.Number.String(), "state-id": "1", // needed so policy can Open config }) envCfg, err := config.New(config.NoDefaults, envAttrs) c.Assert(err, gc.IsNil) st, m, err := agent.InitializeState(cfg, envCfg, mcfg, mongo.DialOpts{}, environs.NewStatePolicy()) c.Assert(err, gc.IsNil) defer st.Close() err = cfg.Write() c.Assert(err, gc.IsNil) // Check that the environment has been set up. env, err := st.Environment() c.Assert(err, gc.IsNil) uuid, ok := envCfg.UUID() c.Assert(ok, jc.IsTrue) c.Assert(env.UUID(), gc.Equals, uuid) // Check that initial admin user has been set up correctly. s.assertCanLogInAsAdmin(c, pwHash) user, err := st.User(env.Owner()) c.Assert(err, gc.IsNil) c.Assert(user.PasswordValid(testing.DefaultMongoPassword), jc.IsTrue) // Check that environment configuration has been added. newEnvCfg, err := st.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(newEnvCfg.AllAttrs(), gc.DeepEquals, envCfg.AllAttrs()) // Check that the bootstrap machine looks correct. c.Assert(m.Id(), gc.Equals, "0") c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageEnviron}) c.Assert(m.Series(), gc.Equals, version.Current.Series) c.Assert(m.CheckProvisioned(agent.BootstrapNonce), jc.IsTrue) c.Assert(m.Addresses(), gc.DeepEquals, mcfg.Addresses) gotConstraints, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(gotConstraints, gc.DeepEquals, expectConstraints) c.Assert(err, gc.IsNil) gotHW, err := m.HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Assert(*gotHW, gc.DeepEquals, expectHW) gotAddrs := m.Addresses() c.Assert(gotAddrs, gc.DeepEquals, mcfg.Addresses) // Check that the API host ports are initialised correctly. apiHostPorts, err := st.APIHostPorts() c.Assert(err, gc.IsNil) c.Assert(apiHostPorts, jc.DeepEquals, [][]network.HostPort{ network.AddressesWithPort( network.NewAddresses("zeroonetwothree", "0.1.2.3"), 1234), }) // Check that the state serving info is initialised correctly. stateServingInfo, err := st.StateServingInfo() c.Assert(err, gc.IsNil) c.Assert(stateServingInfo, jc.DeepEquals, state.StateServingInfo{ APIPort: 1234, StatePort: s.mgoInst.Port(), Cert: testing.ServerCert, PrivateKey: testing.ServerKey, SharedSecret: "abc123", SystemIdentity: "def456", }) // Check that the machine agent's config has been written // and that we can use it to connect to the state. machine0 := names.NewMachineTag("0") newCfg, err := agent.ReadConfig(agent.ConfigPath(dataDir, machine0)) c.Assert(err, gc.IsNil) c.Assert(newCfg.Tag(), gc.Equals, machine0) c.Assert(agent.Password(newCfg), gc.Not(gc.Equals), pwHash) c.Assert(agent.Password(newCfg), gc.Not(gc.Equals), testing.DefaultMongoPassword) info, ok := cfg.MongoInfo() c.Assert(ok, jc.IsTrue) st1, err := state.Open(info, mongo.DialOpts{}, environs.NewStatePolicy()) c.Assert(err, gc.IsNil) defer st1.Close() }
func (s *bootstrapSuite) testInitializeState(c *gc.C, fakeLocalEnv bool) { dataDir := c.MkDir() lxcFakeNetConfig := filepath.Join(c.MkDir(), "lxc-net") netConf := []byte(` # comments ignored LXC_BR= ignored LXC_ADDR = "fooo" LXC_BRIDGE="foobar" # detected anything else ignored LXC_BRIDGE="ignored"`[1:]) err := ioutil.WriteFile(lxcFakeNetConfig, netConf, 0644) c.Assert(err, jc.ErrorIsNil) s.PatchValue(&network.InterfaceByNameAddrs, func(name string) ([]net.Addr, error) { c.Assert(name, gc.Equals, "foobar") return []net.Addr{ &net.IPAddr{IP: net.IPv4(10, 0, 3, 1)}, &net.IPAddr{IP: net.IPv4(10, 0, 3, 4)}, }, nil }) s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig) s.PatchValue(agent.IsLocalEnv, func(*config.Config) bool { c.Logf("fakeLocalEnv=%v", fakeLocalEnv) return fakeLocalEnv }) pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt) configParams := agent.AgentConfigParams{ Paths: agent.Paths{DataDir: dataDir}, Tag: names.NewMachineTag("0"), UpgradedToVersion: version.Current.Number, StateAddresses: []string{s.mgoInst.Addr()}, CACert: testing.CACert, Password: pwHash, Environment: testing.EnvironmentTag, } servingInfo := params.StateServingInfo{ Cert: testing.ServerCert, PrivateKey: testing.ServerKey, CAPrivateKey: testing.CAKey, APIPort: 1234, StatePort: s.mgoInst.Port(), SystemIdentity: "def456", } cfg, err := agent.NewStateMachineConfig(configParams, servingInfo) c.Assert(err, jc.ErrorIsNil) _, available := cfg.StateServingInfo() c.Assert(available, jc.IsTrue) expectConstraints := constraints.MustParse("mem=1024M") expectHW := instance.MustParseHardware("mem=2048M") initialAddrs := network.NewAddresses( "zeroonetwothree", "0.1.2.3", "10.0.3.1", // lxc bridge address filtered (when fakeLocalEnv=false). "10.0.3.4", // lxc bridge address filtered (-"-). "10.0.3.3", // not a lxc bridge address ) mcfg := agent.BootstrapMachineConfig{ Addresses: initialAddrs, Constraints: expectConstraints, Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageEnviron}, InstanceId: "i-bootstrap", Characteristics: expectHW, SharedSecret: "abc123", } filteredAddrs := network.NewAddresses( "zeroonetwothree", "0.1.2.3", "10.0.3.3", ) if fakeLocalEnv { // For local environments - no filtering. filteredAddrs = append([]network.Address{}, initialAddrs...) } envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{ "agent-version": version.Current.Number.String(), "state-id": "1", // needed so policy can Open config }) envCfg, err := config.New(config.NoDefaults, envAttrs) c.Assert(err, jc.ErrorIsNil) adminUser := names.NewLocalUserTag("agent-admin") st, m, err := agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, jc.ErrorIsNil) defer st.Close() err = cfg.Write() c.Assert(err, jc.ErrorIsNil) // Check that the environment has been set up. env, err := st.Environment() c.Assert(err, jc.ErrorIsNil) uuid, ok := envCfg.UUID() c.Assert(ok, jc.IsTrue) c.Assert(env.UUID(), gc.Equals, uuid) // Check that initial admin user has been set up correctly. envTag := env.Tag().(names.EnvironTag) s.assertCanLogInAsAdmin(c, envTag, pwHash) user, err := st.User(env.Owner()) c.Assert(err, jc.ErrorIsNil) c.Assert(user.PasswordValid(testing.DefaultMongoPassword), jc.IsTrue) // Check that environment configuration has been added. newEnvCfg, err := st.EnvironConfig() c.Assert(err, jc.ErrorIsNil) c.Assert(newEnvCfg.AllAttrs(), gc.DeepEquals, envCfg.AllAttrs()) // Check that the bootstrap machine looks correct. c.Assert(m.Id(), gc.Equals, "0") c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageEnviron}) c.Assert(m.Series(), gc.Equals, series.HostSeries()) c.Assert(m.CheckProvisioned(agent.BootstrapNonce), jc.IsTrue) c.Assert(m.Addresses(), jc.DeepEquals, filteredAddrs) gotConstraints, err := m.Constraints() c.Assert(err, jc.ErrorIsNil) c.Assert(gotConstraints, gc.DeepEquals, expectConstraints) c.Assert(err, jc.ErrorIsNil) gotHW, err := m.HardwareCharacteristics() c.Assert(err, jc.ErrorIsNil) c.Assert(*gotHW, gc.DeepEquals, expectHW) // Check that the API host ports are initialised correctly. apiHostPorts, err := st.APIHostPorts() c.Assert(err, jc.ErrorIsNil) c.Assert(apiHostPorts, jc.DeepEquals, [][]network.HostPort{ network.AddressesWithPort(filteredAddrs, 1234), }) // Check that the state serving info is initialised correctly. stateServingInfo, err := st.StateServingInfo() c.Assert(err, jc.ErrorIsNil) c.Assert(stateServingInfo, jc.DeepEquals, state.StateServingInfo{ APIPort: 1234, StatePort: s.mgoInst.Port(), Cert: testing.ServerCert, PrivateKey: testing.ServerKey, CAPrivateKey: testing.CAKey, SharedSecret: "abc123", SystemIdentity: "def456", }) // Check that the machine agent's config has been written // and that we can use it to connect to the state. machine0 := names.NewMachineTag("0") newCfg, err := agent.ReadConfig(agent.ConfigPath(dataDir, machine0)) c.Assert(err, jc.ErrorIsNil) c.Assert(newCfg.Tag(), gc.Equals, machine0) c.Assert(agent.Password(newCfg), gc.Not(gc.Equals), pwHash) c.Assert(agent.Password(newCfg), gc.Not(gc.Equals), testing.DefaultMongoPassword) info, ok := cfg.MongoInfo() c.Assert(ok, jc.IsTrue) st1, err := state.Open(newCfg.Environment(), info, mongo.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, jc.ErrorIsNil) defer st1.Close() }
// Run initializes state for an environment. func (c *BootstrapCommand) Run(_ *cmd.Context) error { envCfg, err := config.New(config.NoDefaults, c.EnvConfig) if err != nil { return err } err = c.ReadConfig("machine-0") if err != nil { return err } agentConfig := c.CurrentConfig() // agent.Jobs is an optional field in the agent config, and was // introduced after 1.17.2. We default to allowing units on // machine-0 if missing. jobs := agentConfig.Jobs() if len(jobs) == 0 { jobs = []params.MachineJob{ params.JobManageEnviron, params.JobHostUnits, } } // Get the bootstrap machine's addresses from the provider. env, err := environs.New(envCfg) if err != nil { return err } instanceId := instance.Id(c.InstanceId) instances, err := env.Instances([]instance.Id{instanceId}) if err != nil { return err } addrs, err := instances[0].Addresses() if err != nil { return err } // Create system-identity file if err := agent.WriteSystemIdentityFile(agentConfig); err != nil { return err } // Generate a shared secret for the Mongo replica set, and write it out. sharedSecret, err := mongo.GenerateSharedSecret() if err != nil { return err } info, ok := agentConfig.StateServingInfo() if !ok { return fmt.Errorf("bootstrap machine config has no state serving info") } info.SharedSecret = sharedSecret err = c.ChangeConfig(func(agentConfig agent.ConfigSetter) { agentConfig.SetStateServingInfo(info) }) if err != nil { return fmt.Errorf("cannot write agent config: %v", err) } agentConfig = c.CurrentConfig() if err := c.startMongo(addrs, agentConfig); err != nil { return err } logger.Infof("started mongo") // Initialise state, and store any agent config (e.g. password) changes. var st *state.State var m *state.Machine err = nil writeErr := c.ChangeConfig(func(agentConfig agent.ConfigSetter) { st, m, err = agent.InitializeState( agentConfig, envCfg, agent.BootstrapMachineConfig{ Addresses: addrs, Constraints: c.Constraints, Jobs: jobs, InstanceId: instanceId, Characteristics: c.Hardware, SharedSecret: sharedSecret, }, mongo.DefaultDialOpts(), environs.NewStatePolicy(), ) }) if writeErr != nil { return fmt.Errorf("cannot write initial configuration: %v", err) } if err != nil { return err } defer st.Close() // bootstrap machine always gets the vote return m.SetHasVote(true) }