func (s *upgradeSuite) attemptRestrictedAPIAsUser(c *gc.C, conf agent.Config) error { info, ok := conf.APIInfo() c.Assert(ok, jc.IsTrue) info.Tag = s.AdminUserTag(c) info.Password = "******" info.Nonce = "" apiState, err := api.Open(info, upgradeTestDialOpts) if err != nil { // If space discovery is in progress we'll get an error here // and need to retry. return err } defer apiState.Close() // This call should always work, but might fail if the apiserver // is restarting. If it fails just return the error so retries // can continue. err = apiState.APICall("Client", 1, "", "FullStatus", nil, new(params.FullStatus)) if err != nil { return errors.Annotate(err, "FullStatus call") } // this call should only work if API is not restricted err = apiState.APICall("Client", 1, "", "WatchAll", nil, nil) return errors.Annotate(err, "WatchAll call") }
func canLoginToAPIAsMachine(c *gc.C, fromConf, toConf agent.Config) bool { info := fromConf.APIInfo() info.Addrs = toConf.APIInfo().Addrs apiState, err := api.Open(info, upgradeTestDialOpts) if apiState != nil { apiState.Close() } return apiState != nil && err == nil }
// OpenAPIState opens the API using the given information. The agent's // password is changed if the fallback password was used to connect to // the API. func OpenAPIState(agentConfig agent.Config, a Agent) (_ *api.State, _ *apiagent.Entity, outErr error) { info := agentConfig.APIInfo() st, usedOldPassword, err := openAPIStateUsingInfo(info, a, agentConfig.OldPassword()) if err != nil { return nil, nil, err } defer func() { if outErr != nil && st != nil { st.Close() } }() entity, err := st.Agent().Entity(a.Tag()) if err == nil && entity.Life() == params.Dead { logger.Errorf("agent terminating - entity %q is dead", a.Tag()) return nil, nil, worker.ErrTerminateAgent } if params.IsCodeUnauthorized(err) { logger.Errorf("agent terminating due to error returned during entity lookup: %v", err) return nil, nil, worker.ErrTerminateAgent } if err != nil { return nil, nil, err } if !usedOldPassword { // Call set password with the current password. If we've recently // become a state server, this will fix up our credentials in mongo. if err := entity.SetPassword(info.Password); err != nil { return nil, nil, errors.Annotate(err, "can't reset agent password") } } else { // We succeeded in connecting with the fallback // password, so we need to create a new password // for the future. newPassword, err := utils.RandomPassword() if err != nil { return nil, nil, err } err = setAgentPassword(newPassword, info.Password, a, entity) if err != nil { return nil, nil, err } // Reconnect to the API with the new password. st.Close() info.Password = newPassword st, err = apiOpen(info, api.DialOpts{}) if err != nil { return nil, nil, err } } return st, entity, err }
func canLoginToAPIAsMachine(c *gc.C, fromConf, toConf agent.Config) bool { fromInfo, ok := fromConf.APIInfo() c.Assert(ok, jc.IsTrue) toInfo, ok := toConf.APIInfo() c.Assert(ok, jc.IsTrue) fromInfo.Addrs = toInfo.Addrs apiState, err := api.Open(fromInfo, upgradeTestDialOpts) if apiState != nil { apiState.Close() } return apiState != nil && err == nil }
func (s *UpgradeSuite) attemptRestrictedAPIAsUser(c *gc.C, conf agent.Config) error { info := conf.APIInfo() info.Tag = s.AdminUserTag(c) info.Password = "******" info.Nonce = "" apiState, err := api.Open(info, upgradeTestDialOpts) c.Assert(err, jc.ErrorIsNil) defer apiState.Close() // this call should always work var result params.FullStatus err = apiState.APICall("Client", 0, "", "FullStatus", nil, &result) c.Assert(err, jc.ErrorIsNil) // this call should only work if API is not restricted return apiState.APICall("Client", 0, "", "WatchAll", nil, nil) }
// tagUserCredentials is a convenience function that extracts the // tag user and apipassword, required to access mongodb. func tagUserCredentials(conf agent.Config) (string, string, error) { username := conf.Tag().String() var password string // TODO(perrito) we might need an accessor for the actual state password // just in case it ever changes from the same as api password. apiInfo, ok := conf.APIInfo() if ok { password = apiInfo.Password } else { // There seems to be no way to reach this inconsistence other than making a // backup on a machine where these fields are corrupted and even so I find // no reasonable way to reach this state, yet since APIInfo has it as a // possibility I prefer to handle it, we cannot recover from this since // it would mean that the agent.conf is corrupted. return "", "", errors.New("cannot obtain password to access the controller") } return username, password, nil }
func (s *UpgradeSuite) canLoginToAPIAsMachine(c *gc.C, config agent.Config) bool { // Ensure logins are always to the API server (machine-0) info := config.APIInfo() info.Addrs = s.machine0Config.APIInfo().Addrs apiState, err := api.Open(info, upgradeTestDialOpts) if apiState != nil { apiState.Close() } if apiState != nil && err == nil { return true } return false }
func checkLoginToAPIAsUser(c *gc.C, conf agent.Config, expectFullApi exposedAPI) { info := conf.APIInfo() info.Tag = names.NewUserTag("admin") info.Password = "******" info.Nonce = "" apiState, err := api.Open(info, upgradeTestDialOpts) c.Assert(err, gc.IsNil) defer apiState.Close() // this call should always work var result api.Status err = apiState.APICall("Client", 0, "", "FullStatus", nil, &result) c.Assert(err, gc.IsNil) // this call should only work if API is not restricted err = apiState.APICall("Client", 0, "", "DestroyEnvironment", nil, nil) if expectFullApi { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, "upgrade in progress .+") } }
func (s *upgradeSuite) attemptRestrictedAPIAsUser(c *gc.C, conf agent.Config) error { info, ok := conf.APIInfo() c.Assert(ok, jc.IsTrue) info.Tag = s.AdminUserTag(c) info.Password = "******" info.Nonce = "" apiState, err := api.Open(info, upgradeTestDialOpts) if err != nil { // If space discovery is in progress we'll get an error here // and need to retry. return err } defer apiState.Close() // this call should always work var result params.FullStatus err = apiState.APICall("Client", 1, "", "FullStatus", nil, &result) c.Assert(err, jc.ErrorIsNil) // this call should only work if API is not restricted return apiState.APICall("Client", 1, "", "WatchAll", nil, nil) }
// newDialInfo returns mgo.DialInfo with the given address using the minimal // possible setup. func newDialInfo(privateAddr string, conf agent.Config) (*mgo.DialInfo, error) { dialOpts := mongo.DialOpts{Direct: true} ssi, ok := conf.StateServingInfo() if !ok { return nil, errors.Errorf("cannot get state serving info to dial") } info := mongo.Info{ Addrs: []string{net.JoinHostPort(privateAddr, strconv.Itoa(ssi.StatePort))}, CACert: conf.CACert(), } dialInfo, err := mongo.DialInfo(info, dialOpts) if err != nil { return nil, errors.Annotate(err, "cannot produce a dial info") } oldPassword := conf.OldPassword() if oldPassword != "" { dialInfo.Username = "******" dialInfo.Password = conf.OldPassword() } else { dialInfo.Username = conf.Tag().String() // TODO(perrito) we might need an accessor for the actual state password // just in case it ever changes from the same as api password. apiInfo, ok := conf.APIInfo() if ok { dialInfo.Password = apiInfo.Password logger.Infof("using API password to access state server.") } else { // There seems to be no way to reach this inconsistence other than making a // backup on a machine where these fields are corrupted and even so I find // no reasonable way to reach this state, yet since APIInfo has it as a // possibility I prefer to handle it, we cannot recover from this since // it would mean that the agent.conf is corrupted. return nil, errors.New("cannot obtain password to access the state server") } } return dialInfo, nil }
func canLoginToAPIAsMachine(c *gc.C, fromConf, toConf agent.Config) bool { fromInfo, ok := fromConf.APIInfo() c.Assert(ok, jc.IsTrue) toInfo, ok := toConf.APIInfo() c.Assert(ok, jc.IsTrue) fromInfo.Addrs = toInfo.Addrs var err error var apiState api.Connection for a := ShortAttempt.Start(); a.Next(); { apiState, err = api.Open(fromInfo, upgradeTestDialOpts) // If space discovery is still in progress we retry. if err != nil && strings.Contains(err.Error(), "spaces are still being discovered") { if !a.HasNext() { return false } continue } if apiState != nil { apiState.Close() } break } return apiState != nil && err == nil }
func (s *UpgradeSuite) canLoginToAPIAsMachine(c *gc.C, config agent.Config) bool { // Ensure logins are always to the API server (machine-0) info := config.APIInfo() info.Addrs = s.machine0Config.APIInfo().Addrs return s.canLoginToAPI(info) }
// openAPIState opens the API using the given information, and // returns the opened state and the api entity with // the given tag. The given changeConfig function is // called if the password changes to set the password. func openAPIState(agentConfig agent.Config, a Agent) (_ *api.State, _ *apiagent.Entity, resultErr error) { // We let the API dial fail immediately because the // runner's loop outside the caller of openAPIState will // keep on retrying. If we block for ages here, // then the worker that's calling this cannot // be interrupted. info := agentConfig.APIInfo() st, err := apiOpen(info, api.DialOpts{}) usedOldPassword := false if params.IsCodeUnauthorized(err) { // We've perhaps used the wrong password, so // try again with the fallback password. infoCopy := *info info = &infoCopy info.Password = agentConfig.OldPassword() usedOldPassword = true st, err = apiOpen(info, api.DialOpts{}) } // The provisioner may take some time to record the agent's // machine instance ID, so wait until it does so. if params.IsCodeNotProvisioned(err) { for a := checkProvisionedStrategy.Start(); a.Next(); { st, err = apiOpen(info, api.DialOpts{}) if !params.IsCodeNotProvisioned(err) { break } } } if err != nil { if params.IsCodeNotProvisioned(err) { return nil, nil, worker.ErrTerminateAgent } if params.IsCodeUnauthorized(err) { return nil, nil, worker.ErrTerminateAgent } return nil, nil, err } defer func() { if resultErr != nil && st != nil { st.Close() } }() entity, err := st.Agent().Entity(a.Tag()) if err == nil && entity.Life() == params.Dead { return nil, nil, worker.ErrTerminateAgent } if err != nil { if params.IsCodeUnauthorized(err) { return nil, nil, worker.ErrTerminateAgent } return nil, nil, err } if usedOldPassword { // We succeeded in connecting with the fallback // password, so we need to create a new password // for the future. newPassword, err := utils.RandomPassword() if err != nil { return nil, nil, err } // Change the configuration *before* setting the entity // password, so that we avoid the possibility that // we might successfully change the entity's // password but fail to write the configuration, // thus locking us out completely. if err := a.ChangeConfig(func(c agent.ConfigSetter) error { c.SetPassword(newPassword) c.SetOldPassword(info.Password) return nil }); err != nil { return nil, nil, err } if err := entity.SetPassword(newPassword); err != nil { return nil, nil, err } st.Close() info.Password = newPassword st, err = apiOpen(info, api.DialOpts{}) if err != nil { return nil, nil, err } } return st, entity, nil }