// OpenAPI tries to open the state using the given Conf. If it // returns a non-empty newPassword, the password used to connect // to the state should be changed accordingly - the caller should write the // configuration with StateInfo.Password set to newPassword, then // set the entity's password accordingly. func (c *Conf) OpenAPI(dialOpts api.DialOpts) (st *api.State, newPassword string, err error) { info := *c.APIInfo info.Nonce = c.MachineNonce if info.Password != "" { st, err := api.Open(&info, dialOpts) if err == nil { return st, "", nil } if params.ErrCode(err) != params.CodeUnauthorized { return nil, "", err } // Access isn't authorized even though we have a password // This can happen if we crash after saving the // password but before changing it, so we'll try again // with the old password. } info.Password = c.OldPassword st, err = api.Open(&info, dialOpts) if err != nil { return nil, "", err } // We've succeeded in connecting with the old password, so // we can now change it to something more private. password, err := utils.RandomPassword() if err != nil { st.Close() return nil, "", err } return st, password, nil }
func (s *serverSuite) TestOpenAsMachineErrors(c *C) { stm, err := s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, IsNil) err = stm.SetProvisioned("foo", "fake_nonce", nil) c.Assert(err, IsNil) err = stm.SetPassword("password") c.Assert(err, IsNil) // This does almost exactly the same as OpenAPIAsMachine but checks // for failures instead. _, info, err := s.APIConn.Environ.StateInfo() info.Tag = stm.Tag() info.Password = "******" info.Nonce = "invalid-nonce" st, err := api.Open(info, fastDialOpts) c.Assert(err, ErrorMatches, params.CodeNotProvisioned) c.Assert(st, IsNil) // Try with empty nonce as well. info.Nonce = "" st, err = api.Open(info, fastDialOpts) c.Assert(err, ErrorMatches, params.CodeNotProvisioned) c.Assert(st, IsNil) // Finally, with the correct one succeeds. info.Nonce = "fake_nonce" st, err = api.Open(info, fastDialOpts) c.Assert(err, IsNil) c.Assert(st, NotNil) st.Close() // Now add another machine, intentionally unprovisioned. stm1, err := s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, IsNil) err = stm1.SetPassword("password") c.Assert(err, IsNil) // Try connecting, it will fail. info.Tag = stm1.Tag() info.Nonce = "" st, err = api.Open(info, fastDialOpts) c.Assert(err, ErrorMatches, params.CodeNotProvisioned) c.Assert(st, IsNil) }
func (s *MachineSuite) TestManageStateServesAPI(c *C) { s.assertJobWithState(c, state.JobManageState, func(conf *agent.Conf, agentState *state.State) { st, err := api.Open(conf.APIInfo, fastDialOpts) c.Assert(err, IsNil) defer st.Close() m, err := st.Machiner().Machine(conf.APIInfo.Tag) c.Assert(err, IsNil) c.Assert(m.Life(), Equals, params.Alive) }) }
func (s *JujuConnSuite) openAPIAs(c *C, tag, password, nonce string) *api.State { _, info, err := s.APIConn.Environ.StateInfo() c.Assert(err, IsNil) info.Tag = tag info.Password = password info.Nonce = nonce st, err := api.Open(info, api.DialOpts{}) c.Assert(err, IsNil) c.Assert(st, NotNil) return st }
func (s *suite) SetUpTest(c *C) { s.JujuConnSuite.SetUpTest(c) var err error s.srv, err = api.NewServer(s.State, "localhost:0", []byte(coretesting.ServerCert), []byte(coretesting.ServerKey)) c.Assert(err, IsNil) s.APIState, err = api.Open(&api.Info{ Addr: s.srv.Addr(), CACert: []byte(coretesting.CACert), }) c.Assert(err, IsNil) }
// openAs connects to the API state as the given entity // with the default password for that entity. func (s *baseSuite) openAs(c *C, tag string) *api.State { _, info, err := s.APIConn.Environ.StateInfo() c.Assert(err, IsNil) info.Tag = tag info.Password = fmt.Sprintf("%s password", tag) // Set this always, so that the login attempts as a machine will // not fail with ErrNotProvisioned; it's not used otherwise. info.Nonce = "fake_nonce" c.Logf("opening state; entity %q; password %q", info.Tag, info.Password) st, err := api.Open(info, api.DialOpts{}) c.Assert(err, IsNil) c.Assert(st, NotNil) return st }
func (s *loginSuite) TestBadLogin(c *C) { // Start our own server so we can control when the first login // happens. Otherwise in JujuConnSuite.SetUpTest api.Open is // called with user-admin permissions automatically. srv, err := apiserver.NewServer( s.State, "localhost:0", []byte(coretesting.ServerCert), []byte(coretesting.ServerKey), ) c.Assert(err, IsNil) defer func() { err := srv.Stop() c.Assert(err, IsNil) }() info := &api.Info{ Tag: "", Password: "", Addrs: []string{srv.Addr()}, CACert: []byte(coretesting.CACert), } for i, t := range badLoginTests { c.Logf("test %d; entity %q; password %q", i, t.tag, t.password) // Note that Open does not log in if the tag and password // are empty. This allows us to test operations on the connection // before calling Login, which we could not do if Open // always logged in. info.Tag = "" info.Password = "" func() { st, err := api.Open(info, fastDialOpts) c.Assert(err, IsNil) defer st.Close() _, err = st.Machiner().Machine("0") c.Assert(err, ErrorMatches, `unknown object type "Machiner"`) // Since these are user login tests, the nonce is empty. err = st.Login(t.tag, t.password, "") c.Assert(err, ErrorMatches, t.err) c.Assert(params.ErrCode(err), Equals, t.code) _, err = st.Machiner().Machine("0") c.Assert(err, ErrorMatches, `unknown object type "Machiner"`) }() } }
func (s *serverSuite) TestStop(c *C) { // Start our own instance of the server so we have // a handle on it to stop it. srv, err := apiserver.NewServer(s.State, "localhost:0", []byte(coretesting.ServerCert), []byte(coretesting.ServerKey)) c.Assert(err, IsNil) defer srv.Stop() stm, err := s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, IsNil) err = stm.SetProvisioned("foo", "fake_nonce", nil) c.Assert(err, IsNil) err = stm.SetPassword("password") c.Assert(err, IsNil) // Note we can't use openAs because we're not connecting to // s.APIConn. apiInfo := &api.Info{ Tag: stm.Tag(), Password: "******", Nonce: "fake_nonce", Addrs: []string{srv.Addr()}, CACert: []byte(coretesting.CACert), } st, err := api.Open(apiInfo, fastDialOpts) c.Assert(err, IsNil) defer st.Close() _, err = st.Machiner().Machine(stm.Tag()) c.Assert(err, IsNil) err = srv.Stop() c.Assert(err, IsNil) _, err = st.Machiner().Machine(stm.Tag()) // The client has not necessarily seen the server shutdown yet, // so there are two possible errors. if err != rpc.ErrShutdown && err != io.ErrUnexpectedEOF { c.Fatalf("unexpected error from request: %v", err) } // Check it can be stopped twice. err = srv.Stop() c.Assert(err, IsNil) }
// NewAPIConn returns a new Conn that uses the // given environment. The environment must have already // been bootstrapped. func NewAPIConn(environ environs.Environ, dialOpts api.DialOpts) (*APIConn, error) { _, info, err := environ.StateInfo() if err != nil { return nil, err } info.Tag = "user-admin" password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } info.Password = password st, err := api.Open(info, dialOpts) // TODO(rog): handle errUnauthorized when the API handles passwords. if err != nil { return nil, err } // TODO(rog): implement updateSecrets (see Conn.updateSecrets) return &APIConn{ Environ: environ, State: st, }, nil }