func (c *restoreCommand) Run(ctx *cmd.Context) error { if c.showDescription { fmt.Fprintf(ctx.Stdout, "%s\n", c.Info().Purpose) return nil } if err := c.Log.Start(ctx); err != nil { return err } agentConf, err := extractConfig(c.backupFile) if err != nil { return errors.Annotate(err, "cannot extract configuration from backup file") } progress("extracted credentials from backup file") store, err := configstore.Default() if err != nil { return err } cfg, err := c.Config(store) if err != nil { return err } env, err := rebootstrap(cfg, ctx, c.Constraints) if err != nil { return errors.Annotate(err, "cannot re-bootstrap environment") } progress("connecting to newly bootstrapped instance") var apiState *api.State // The state server backend may not be ready to accept logins so we retry. // We'll do up to 8 retries over 2 minutes to give the server time to come up. // Typically we expect only 1 retry will be needed. attempt := utils.AttemptStrategy{Delay: 15 * time.Second, Min: 8} for a := attempt.Start(); a.Next(); { apiState, err = juju.NewAPIState(env, api.DefaultDialOpts()) if err == nil || errors.Cause(err).Error() != "EOF" { break } progress("bootstrapped instance not ready - attempting to redial") } if err != nil { return errors.Annotate(err, "cannot connect to bootstrap instance") } progress("restoring bootstrap machine") machine0Addr, err := restoreBootstrapMachine(apiState, c.backupFile, agentConf) if err != nil { return errors.Annotate(err, "cannot restore bootstrap machine") } progress("restored bootstrap machine") apiState, err = juju.NewAPIState(env, api.DefaultDialOpts()) progress("opening state") if err != nil { return errors.Annotate(err, "cannot connect to api server") } progress("updating all machines") if err := updateAllMachines(apiState, machine0Addr); err != nil { return errors.Annotate(err, "cannot update machines") } return nil }
func newAPIConnectionFromNames( c *gc.C, controller, account, model string, store jujuclient.ClientStore, apiOpen api.OpenFunc, getBootstrapConfig func(string) (*config.Config, error), ) (api.Connection, error) { params := juju.NewAPIConnectionParams{ Store: store, ControllerName: controller, BootstrapConfig: getBootstrapConfig, DialOpts: api.DefaultDialOpts(), } if account != "" { accountDetails, err := store.AccountByName(controller, account) c.Assert(err, jc.ErrorIsNil) params.AccountDetails = accountDetails } if model != "" { modelDetails, err := store.ModelByName(controller, account, model) c.Assert(err, jc.ErrorIsNil) params.ModelUUID = modelDetails.ModelUUID } return juju.NewAPIFromStore(params, apiOpen) }
// apiInfoConnect looks for endpoint on the given environment and // tries to connect to it, sending the result on the returned channel. func apiInfoConnect(store configstore.Storage, info configstore.EnvironInfo, apiOpen apiOpenFunc, stop <-chan struct{}) (apiState, error) { endpoint := info.APIEndpoint() if info == nil || len(endpoint.Addresses) == 0 { return nil, &infoConnectError{fmt.Errorf("no cached addresses")} } logger.Infof("connecting to API addresses: %v", endpoint.Addresses) var environTag names.Tag if endpoint.EnvironUUID != "" { // Note: we should be validating that EnvironUUID contains a // valid UUID. environTag = names.NewEnvironTag(endpoint.EnvironUUID) } username := info.APICredentials().User if username == "" { username = "******" } apiInfo := &api.Info{ Addrs: endpoint.Addresses, CACert: endpoint.CACert, Tag: names.NewUserTag(username), Password: info.APICredentials().Password, EnvironTag: environTag, } st, err := apiOpen(apiInfo, api.DefaultDialOpts()) if err != nil { return nil, &infoConnectError{err} } return st, nil }
func (s *undertakerSuite) hostedAPI(c *gc.C) (*undertaker.Client, *state.State) { otherState := s.Factory.MakeModel(c, &factory.ModelParams{Name: "hosted_env"}) password, err := utils.RandomPassword() c.Assert(err, jc.ErrorIsNil) machine := s.Factory.MakeMachine(c, &factory.MachineParams{ Jobs: []state.MachineJob{state.JobManageModel}, Password: password, Nonce: "fake_nonce", }) // Connect to hosted environ from controller. info := s.APIInfo(c) info.Tag = machine.Tag() info.Password = password info.Nonce = "fake_nonce" info.ModelTag = otherState.ModelTag() otherAPIState, err := api.Open(info, api.DefaultDialOpts()) c.Assert(err, jc.ErrorIsNil) undertakerClient := undertaker.NewClient(otherAPIState) c.Assert(undertakerClient, gc.NotNil) return undertakerClient, otherState }
func dialLogsinkAPI(apiInfo *api.Info) (*websocket.Conn, error) { // TODO(mjs) Most of this should be extracted to be shared for // connections to both /log (debuglog) and /logsink. header := utils.BasicAuthHeader(apiInfo.Tag.String(), apiInfo.Password) header.Set("X-Juju-Nonce", apiInfo.Nonce) conn, err := api.Connect(apiInfo, "/logsink", header, api.DefaultDialOpts()) if err != nil { return nil, errors.Annotate(err, "failed to connect to logsink API") } // Read the initial error and translate to a real error. // Read up to the first new line character. We can't use bufio here as it // reads too much from the reader. line := make([]byte, 4096) n, err := conn.Read(line) if err != nil { return nil, errors.Annotate(err, "unable to read initial response") } line = line[0:n] var errResult params.ErrorResult err = json.Unmarshal(line, &errResult) if err != nil { return nil, errors.Annotate(err, "unable to unmarshal initial response") } if errResult.Error != nil { return nil, errors.Annotatef(err, "initial server error") } return conn, nil }
// apiInfoConnect looks for endpoint on the given environment and // tries to connect to it, sending the result on the returned channel. func apiInfoConnect(info configstore.EnvironInfo, apiOpen api.OpenFunc, stop <-chan struct{}, bClient *httpbakery.Client) (api.Connection, error) { endpoint := info.APIEndpoint() if info == nil || len(endpoint.Addresses) == 0 { return nil, &infoConnectError{fmt.Errorf("no cached addresses")} } logger.Infof("connecting to API addresses: %v", endpoint.Addresses) var modelTag names.ModelTag if names.IsValidModel(endpoint.ModelUUID) { modelTag = names.NewModelTag(endpoint.ModelUUID) } apiInfo := &api.Info{ Addrs: endpoint.Addresses, CACert: endpoint.CACert, Tag: environInfoUserTag(info), Password: info.APICredentials().Password, ModelTag: modelTag, } if apiInfo.Tag == nil { apiInfo.UseMacaroons = true } dialOpts := api.DefaultDialOpts() dialOpts.BakeryClient = bClient st, err := apiOpen(apiInfo, dialOpts) if err != nil { return nil, &infoConnectError{err} } return st, nil }
func (s *Suite) TestMigration(c *gc.C) { masterClient := newStubMasterClient(s.stub) w := migrationmaster.New(masterClient) // Trigger migration. masterClient.watcher.changes <- migration.TargetInfo{ ControllerTag: names.NewModelTag("uuid"), Addrs: []string{"1.2.3.4:5"}, CACert: "cert", AuthTag: names.NewUserTag("admin"), Password: "******", } // This error is temporary while migrationmaster is a WIP. runWorkerAndWait(c, w, "migration seen and aborted") // Observe that the migration was seen, the model exported, an API // connection to the target controller was made, the model was // imported and then the migration aborted. s.stub.CheckCalls(c, []jujutesting.StubCall{ {"masterClient.Watch", nil}, {"masterClient.Export", nil}, {"apiOpen", []interface{}{&api.Info{ Addrs: []string{"1.2.3.4:5"}, CACert: "cert", Tag: names.NewUserTag("admin"), Password: "******", }, api.DefaultDialOpts()}}, {"APICall:MigrationTarget.Import", []interface{}{params.SerializedModel{Bytes: fakeSerializedModel}}}, {"masterClient.SetPhase", []interface{}{migration.ABORT}}, {"Connection.Close", nil}, }) }
// apiInfoConnect looks for endpoint on the given environment and // tries to connect to it, sending the result on the returned channel. func apiInfoConnect(store configstore.Storage, info configstore.EnvironInfo, apiOpen apiOpenFunc, stop <-chan struct{}) (apiState, error) { endpoint := info.APIEndpoint() if info == nil || len(endpoint.Addresses) == 0 { return nil, &infoConnectError{fmt.Errorf("no cached addresses")} } logger.Infof("connecting to API addresses: %v", endpoint.Addresses) var environTag names.EnvironTag if names.IsValidEnvironment(endpoint.EnvironUUID) { environTag = names.NewEnvironTag(endpoint.EnvironUUID) } else { // For backwards-compatibility, we have to allow connections // with an empty UUID. Login will work for the same reasons. logger.Warningf("ignoring invalid API endpoint environment UUID %v", endpoint.EnvironUUID) } apiInfo := &api.Info{ Addrs: endpoint.Addresses, CACert: endpoint.CACert, Tag: environInfoUserTag(info), Password: info.APICredentials().Password, EnvironTag: environTag, } st, err := apiOpen(apiInfo, api.DefaultDialOpts()) if err != nil { return nil, &infoConnectError{err} } return st, nil }
func (s *cmdControllerSuite) TestAddModel(c *gc.C) { // The JujuConnSuite doesn't set up an ssh key in the fake home dir, // so fake one on the command line. The dummy provider also expects // a config value for 'controller'. context := s.run(c, "add-model", "new-model", "authorized-keys=fake-key", "controller=false") c.Check(testing.Stdout(context), gc.Equals, "") c.Check(testing.Stderr(context), gc.Equals, "added model \"new-model\"\n") // Make sure that the saved server details are sufficient to connect // to the api server. accountDetails, err := s.ControllerStore.AccountByName("kontroll", "admin@local") c.Assert(err, jc.ErrorIsNil) modelDetails, err := s.ControllerStore.ModelByName("kontroll", "admin@local", "new-model") c.Assert(err, jc.ErrorIsNil) api, err := juju.NewAPIConnection(juju.NewAPIConnectionParams{ Store: s.ControllerStore, ControllerName: "kontroll", AccountDetails: accountDetails, ModelUUID: modelDetails.ModelUUID, BootstrapConfig: noBootstrapConfig, DialOpts: api.DefaultDialOpts(), }) c.Assert(err, jc.ErrorIsNil) api.Close() }
func (s *cmdControllerSuite) testAddModel(c *gc.C, args ...string) { // The JujuConnSuite doesn't set up an ssh key in the fake home dir, // so fake one on the command line. The dummy provider also expects // a config value for 'controller'. args = append([]string{"add-model", "new-model"}, args...) args = append(args, "--config", "authorized-keys=fake-key", "--config", "controller=false", ) context := s.run(c, args...) c.Check(testing.Stdout(context), gc.Equals, "") c.Check(testing.Stderr(context), gc.Equals, ` Added 'new-model' model on dummy/dummy-region with credential 'cred' for user 'admin' `[1:]) // Make sure that the saved server details are sufficient to connect // to the api server. accountDetails, err := s.ControllerStore.AccountDetails("kontroll") c.Assert(err, jc.ErrorIsNil) modelDetails, err := s.ControllerStore.ModelByName("kontroll", "admin@local/new-model") c.Assert(err, jc.ErrorIsNil) api, err := juju.NewAPIConnection(juju.NewAPIConnectionParams{ Store: s.ControllerStore, ControllerName: "kontroll", AccountDetails: accountDetails, ModelUUID: modelDetails.ModelUUID, DialOpts: api.DefaultDialOpts(), OpenAPI: api.Open, }) c.Assert(err, jc.ErrorIsNil) api.Close() }
func (s *apiEnvironmentSuite) TestUploadToolsOtherEnvironment(c *gc.C) { // setup other environment otherState := s.Factory.MakeModel(c, nil) defer otherState.Close() info := s.APIInfo(c) info.ModelTag = otherState.ModelTag() otherAPIState, err := api.Open(info, api.DefaultDialOpts()) c.Assert(err, jc.ErrorIsNil) defer otherAPIState.Close() otherClient := otherAPIState.Client() defer otherClient.ClientFacade.Close() newVersion := version.MustParseBinary("5.4.3-quantal-amd64") vers := newVersion.String() // build fake tools tgz, checksum := coretesting.TarGz( coretesting.NewTarFile(jujunames.Jujud, 0777, "jujud contents "+vers)) toolsList, err := otherClient.UploadTools(bytes.NewReader(tgz), newVersion) c.Assert(err, jc.ErrorIsNil) c.Assert(toolsList, gc.HasLen, 1) c.Assert(toolsList[0].SHA256, gc.Equals, checksum) toolStrg, err := otherState.ToolsStorage() defer toolStrg.Close() c.Assert(err, jc.ErrorIsNil) meta, closer, err := toolStrg.Open(vers) defer closer.Close() c.Assert(err, jc.ErrorIsNil) c.Assert(meta.SHA256, gc.Equals, checksum) c.Assert(meta.Version, gc.Equals, vers) }
func (c *registerCommand) secretKeyLogin(addrs []string, request params.SecretKeyLoginRequest) (*params.SecretKeyLoginResponse, error) { buf, err := json.Marshal(&request) if err != nil { return nil, errors.Annotate(err, "marshalling request") } r := bytes.NewReader(buf) // Determine which address to use by attempting to open an API // connection with each of the addresses. Note that we do not // know the CA certificate yet, so we do not want to send any // sensitive information. We make no attempt to log in until // we can verify the server's identity. opts := api.DefaultDialOpts() opts.InsecureSkipVerify = true conn, err := c.apiOpen(&api.Info{ Addrs: addrs, SkipLogin: true, // NOTE(axw) CACert is required, but ignored if // InsecureSkipVerify is set. We should try to // bring together CACert and InsecureSkipVerify // so they can be validated together. CACert: "ignored", }, opts) if err != nil { return nil, errors.Trace(err) } apiAddr := conn.Addr() if err := conn.Close(); err != nil { return nil, errors.Trace(err) } // Using the address we connected to above, perform the request. urlString := fmt.Sprintf("https://%s/register", apiAddr) httpReq, err := http.NewRequest("POST", urlString, r) if err != nil { return nil, errors.Trace(err) } httpReq.Header.Set("Content-Type", "application/json") httpClient := utils.GetNonValidatingHTTPClient() httpResp, err := httpClient.Do(httpReq) if err != nil { return nil, errors.Trace(err) } defer httpResp.Body.Close() if httpResp.StatusCode != http.StatusOK { var resp params.ErrorResult if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil { return nil, errors.Trace(err) } return nil, resp.Error } var resp params.SecretKeyLoginResponse if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil { return nil, errors.Trace(err) } return &resp, nil }
func (s *clientSuite) otherEnviron(c *gc.C) (*state.State, *api.State) { otherSt := s.Factory.MakeEnvironment(c, nil) info := s.APIInfo(c) info.EnvironTag = otherSt.EnvironTag() apiState, err := api.Open(info, api.DefaultDialOpts()) c.Assert(err, jc.ErrorIsNil) return otherSt, apiState }
func (s *clientSuite) otherModel(c *gc.C) (*state.State, api.Connection) { otherSt := s.Factory.MakeModel(c, nil) info := s.APIInfo(c) info.ModelTag = otherSt.ModelTag() apiState, err := api.Open(info, api.DefaultDialOpts()) c.Assert(err, jc.ErrorIsNil) return otherSt, apiState }
func (s *pingerSuite) TestClientNoNeedToPing(c *gc.C) { s.PatchValue(apiserver.MaxClientPingInterval, time.Duration(0)) st, err := api.Open(s.APIInfo(c), api.DefaultDialOpts()) c.Assert(err, jc.ErrorIsNil) defer st.Close() time.Sleep(coretesting.ShortWait) err = st.Ping() c.Assert(err, jc.ErrorIsNil) }
func openAPIConn(targetInfo *migration.TargetInfo) (api.Connection, error) { apiInfo := &api.Info{ Addrs: targetInfo.Addrs, CACert: targetInfo.CACert, Tag: targetInfo.AuthTag, Password: targetInfo.Password, } return apiOpen(apiInfo, api.DefaultDialOpts()) }
func (t *LiveTests) TestCheckEnvironmentOnConnect(c *gc.C) { // When new connection is established to a bootstraped environment, // it is checked that we are running against a juju-core environment. if !t.CanOpenState { c.Skip("CanOpenState is false; cannot open state connection") } t.BootstrapOnce(c) apiState, err := juju.NewAPIState(t.Env, api.DefaultDialOpts()) c.Assert(err, gc.IsNil) apiState.Close() }
func (s *NewAPIClientSuite) TestWithConfigAndNoInfo(c *gc.C) { c.Skip("not really possible now that there is no defined admin user") s.PatchValue(&version.Current, coretesting.FakeVersionNumber) coretesting.MakeSampleJujuHome(c) store := newConfigStore(coretesting.SampleModelName, &environInfo{ bootstrapConfig: map[string]interface{}{ "type": "dummy", "name": "myenv", "state-server": true, "authorized-keys": "i-am-a-key", "default-series": config.LatestLtsSeries(), "firewall-mode": config.FwInstance, "development": false, "ssl-hostname-verification": true, "admin-secret": "adminpass", }, }) s.bootstrapEnv(c, coretesting.SampleModelName, store) info, err := store.ReadInfo("myenv") c.Assert(err, jc.ErrorIsNil) c.Assert(info, gc.NotNil) c.Logf("%#v", info.APICredentials()) called := 0 expectState := mockedAPIState(0) apiOpen := func(apiInfo *api.Info, opts api.DialOpts) (api.Connection, error) { c.Check(apiInfo.Tag, gc.Equals, dummy.AdminUserTag()) c.Check(string(apiInfo.CACert), gc.Not(gc.Equals), "") c.Check(apiInfo.Password, gc.Equals, "adminpass") // ModelTag wasn't in regular Config c.Check(apiInfo.ModelTag.Id(), gc.Equals, "") c.Check(opts, gc.DeepEquals, api.DefaultDialOpts()) called++ return expectState, nil } st, err := juju.NewAPIFromStore("myenv", store, apiOpen) c.Assert(err, jc.ErrorIsNil) c.Assert(st, gc.Equals, expectState) c.Assert(called, gc.Equals, 1) // Make sure the cache is updated. info, err = store.ReadInfo("myenv") c.Assert(err, jc.ErrorIsNil) c.Assert(info, gc.NotNil) ep := info.APIEndpoint() c.Assert(ep.Addresses, gc.HasLen, 1) c.Check(ep.Addresses[0], gc.Matches, `localhost:\d+`) c.Check(ep.CACert, gc.Not(gc.Equals), "") }
func (s *cmdRegistrationSuite) TestAddUserAndRegister(c *gc.C) { // First, add user "bob", and record the "juju register" command // that is printed out. context := s.run(c, nil, "add-user", "bob", "Bob Dobbs") c.Check(testing.Stderr(context), gc.Equals, "") stdout := testing.Stdout(context) c.Check(stdout, gc.Matches, ` User "Bob Dobbs \(bob\)" added Please send this command to bob: juju register .* "Bob Dobbs \(bob\)" has not been granted access to any models(.|\n)* `[1:]) jujuRegisterCommand := strings.Fields(strings.TrimSpace( strings.SplitN(stdout[strings.Index(stdout, "juju register"):], "\n", 2)[0], )) c.Logf("%q", jujuRegisterCommand) // Now run the "juju register" command. We need to pass the // controller name and password to set. stdin := strings.NewReader("bob-controller\nhunter2\nhunter2\n") args := jujuRegisterCommand[1:] // drop the "juju" context = s.run(c, stdin, args...) c.Check(testing.Stdout(context), gc.Equals, "") c.Check(testing.Stderr(context), gc.Equals, ` Please set a name for this controller: Enter password: Confirm password: Welcome, bob. You are now logged into "bob-controller". There are no models available. You can create models with "juju create-model", or you can ask an administrator or owner of a model to grant access to that model with "juju grant". `[1:]) // Make sure that the saved server details are sufficient to connect // to the api server. accountDetails, err := s.ControllerStore.AccountByName("bob-controller", "bob@local") c.Assert(err, jc.ErrorIsNil) api, err := juju.NewAPIConnection(juju.NewAPIConnectionParams{ Store: s.ControllerStore, ControllerName: "bob-controller", AccountDetails: accountDetails, BootstrapConfig: noBootstrapConfig, DialOpts: api.DefaultDialOpts(), }) c.Assert(err, jc.ErrorIsNil) c.Assert(api.Close(), jc.ErrorIsNil) }
func (s *destroyModelSuite) TestBlockDestroyDestroyHostedModel(c *gc.C) { otherSt := s.Factory.MakeModel(c, nil) defer otherSt.Close() info := s.APIInfo(c) info.ModelTag = otherSt.ModelTag() apiState, err := api.Open(info, api.DefaultDialOpts()) block := commontesting.NewBlockHelper(apiState) defer block.Close() block.BlockDestroyModel(c, "TestBlockDestroyDestroyModel") err = common.DestroyModelIncludingHosted(s.State, s.State.ModelTag()) s.AssertBlocked(c, err, "TestBlockDestroyDestroyModel") }
func (s *NewAPIClientSuite) TestWithConfigAndNoInfo(c *gc.C) { coretesting.MakeSampleJujuHome(c) store := newConfigStore(coretesting.SampleEnvName, &environInfo{ bootstrapConfig: map[string]interface{}{ "type": "dummy", "name": "myenv", "state-server": true, "authorized-keys": "i-am-a-key", "default-series": config.LatestLtsSeries(), "firewall-mode": config.FwInstance, "development": false, "ssl-hostname-verification": true, "admin-secret": "adminpass", }, }) bootstrapEnv(c, coretesting.SampleEnvName, store) info, err := store.ReadInfo("myenv") c.Assert(err, gc.IsNil) c.Assert(info, gc.NotNil) called := 0 expectState := mockedAPIState(0) apiOpen := func(apiInfo *api.Info, opts api.DialOpts) (juju.APIState, error) { c.Check(apiInfo.Tag, gc.Equals, names.NewUserTag("admin")) c.Check(string(apiInfo.CACert), gc.Not(gc.Equals), "") c.Check(apiInfo.Password, gc.Equals, "adminpass") // EnvironTag wasn't in regular Config c.Check(apiInfo.EnvironTag, gc.IsNil) c.Check(opts, gc.DeepEquals, api.DefaultDialOpts()) called++ return expectState, nil } st, err := juju.NewAPIFromStore("myenv", store, apiOpen) c.Assert(err, gc.IsNil) c.Assert(st, gc.Equals, expectState) c.Assert(called, gc.Equals, 1) // Make sure the cache is updated. info, err = store.ReadInfo("myenv") c.Assert(err, gc.IsNil) c.Assert(info, gc.NotNil) ep := info.APIEndpoint() c.Assert(ep.Addresses, gc.HasLen, 1) c.Check(ep.Addresses[0], gc.Matches, `localhost:\d+`) c.Check(ep.CACert, gc.Not(gc.Equals), "") }
func (s *workerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) // Create a machine for the client to log in as. nonce := "some-nonce" machine, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{Nonce: nonce}) apiInfo := s.APIInfo(c) apiInfo.Tag = machine.Tag() apiInfo.Password = password apiInfo.Nonce = nonce st, err := api.Open(apiInfo, api.DefaultDialOpts()) c.Assert(err, gc.IsNil) s.APIState = st s.machineTag = machine.Tag() }
// publicControllerDetails returns controller and account details to be registered // for the given public controller host name. func (c *registerCommand) publicControllerDetails(host string) (jujuclient.ControllerDetails, jujuclient.AccountDetails, error) { errRet := func(err error) (jujuclient.ControllerDetails, jujuclient.AccountDetails, error) { return jujuclient.ControllerDetails{}, jujuclient.AccountDetails{}, err } apiAddr := host if !strings.Contains(apiAddr, ":") { apiAddr += ":443" } // Make a direct API connection because we don't yet know the // controller UUID so can't store the thus-incomplete controller // details to make a conventional connection. // // Unfortunately this means we'll connect twice to the controller // but it's probably best to go through the conventional path the // second time. bclient, err := c.BakeryClient() if err != nil { return errRet(errors.Trace(err)) } dialOpts := api.DefaultDialOpts() dialOpts.BakeryClient = bclient conn, err := c.apiOpen(&api.Info{ Addrs: []string{apiAddr}, }, dialOpts) if err != nil { return errRet(errors.Trace(err)) } defer conn.Close() user, ok := conn.AuthTag().(names.UserTag) if !ok { return errRet(errors.Errorf("logged in as %v, not a user", conn.AuthTag())) } // If we get to here, then we have a cached macaroon for the registered // user. If we encounter an error after here, we need to clear it. c.onRunError = func() { if err := c.ClearControllerMacaroons([]string{apiAddr}); err != nil { logger.Errorf("failed to clear macaroon: %v", err) } } return jujuclient.ControllerDetails{ APIEndpoints: []string{apiAddr}, ControllerUUID: conn.ControllerTag().Id(), }, jujuclient.AccountDetails{ User: user.Id(), LastKnownAccess: conn.ControllerAccess(), }, nil }
// apiConfigConnect looks for configuration info on the given environment, // and tries to use an Environ constructed from that to connect to // its endpoint. It only starts the attempt after the given delay, // to allow the faster apiInfoConnect to hopefully succeed first. // It returns nil if there was no configuration information found. func apiConfigConnect(cfg *config.Config, apiOpen apiOpenFunc, stop <-chan struct{}, delay time.Duration, user names.UserTag) (apiState, error) { select { case <-time.After(delay): case <-stop: return nil, errAborted } environ, err := environs.New(cfg) if err != nil { return nil, err } apiInfo, err := environAPIInfo(environ, user) if err != nil { return nil, err } st, err := apiOpen(apiInfo, api.DefaultDialOpts()) // TODO(rog): handle errUnauthorized when the API handles passwords. if err != nil { return nil, err } return apiStateCachedInfo{st, apiInfo}, nil }
func newAPIConnectionParams( store jujuclient.ClientStore, controllerName, modelName string, accountDetails *jujuclient.AccountDetails, bakery *httpbakery.Client, apiOpen api.OpenFunc, getPassword func(string) (string, error), ) (juju.NewAPIConnectionParams, error) { if controllerName == "" { return juju.NewAPIConnectionParams{}, errors.Trace(errNoNameSpecified) } var modelUUID string if modelName != "" { modelDetails, err := store.ModelByName(controllerName, modelName) if err != nil { return juju.NewAPIConnectionParams{}, errors.Trace(err) } modelUUID = modelDetails.ModelUUID } dialOpts := api.DefaultDialOpts() dialOpts.BakeryClient = bakery if accountDetails != nil { bakery.WebPageVisitor = httpbakery.NewMultiVisitor( authentication.NewVisitor(accountDetails.User, getPassword), bakery.WebPageVisitor, ) } return juju.NewAPIConnectionParams{ Store: store, ControllerName: controllerName, AccountDetails: accountDetails, ModelUUID: modelUUID, DialOpts: dialOpts, OpenAPI: apiOpen, }, nil }
func NewContext() (*Context, error) { initialize() jar, err := cookiejar.New(nil) if err != nil { return nil, errors.Trace(err) } bclient := httpbakery.NewClient() bclient.Jar = jar bclient.VisitWebPage = httpbakery.OpenWebBrowser dialOpts := api.DefaultDialOpts() dialOpts.BakeryClient = bclient store := jujuclient.NewFileClientStore() cstore, err := newCacheStore(store) if err != nil { return nil, errors.Annotatef(err, "cannot make store cache") } return &Context{ store: cstore, jar: jar, dialOpts: dialOpts, }, nil }
func newAPIConnectionParams( store jujuclient.ClientStore, controllerName, accountName, modelName string, bakery *httpbakery.Client, ) (juju.NewAPIConnectionParams, error) { if controllerName == "" { return juju.NewAPIConnectionParams{}, errors.Trace(errNoNameSpecified) } var accountDetails *jujuclient.AccountDetails if accountName != "" { var err error accountDetails, err = store.AccountByName(controllerName, accountName) if err != nil { return juju.NewAPIConnectionParams{}, errors.Trace(err) } } var modelUUID string if modelName != "" { modelDetails, err := store.ModelByName(controllerName, accountName, modelName) if err != nil { return juju.NewAPIConnectionParams{}, errors.Trace(err) } modelUUID = modelDetails.ModelUUID } dialOpts := api.DefaultDialOpts() dialOpts.BakeryClient = bakery return juju.NewAPIConnectionParams{ Store: store, ControllerName: controllerName, BootstrapConfig: NewGetBootstrapConfigFunc(store), AccountDetails: accountDetails, ModelUUID: modelUUID, DialOpts: dialOpts, }, nil }
func newAPIConnectionFromNames( c *gc.C, controller, model string, store jujuclient.ClientStore, apiOpen api.OpenFunc, ) (api.Connection, error) { params := juju.NewAPIConnectionParams{ Store: store, ControllerName: controller, DialOpts: api.DefaultDialOpts(), OpenAPI: apiOpen, } accountDetails, err := store.AccountDetails(controller) if !errors.IsNotFound(err) { c.Assert(err, jc.ErrorIsNil) params.AccountDetails = accountDetails } if model != "" { modelDetails, err := store.ModelByName(controller, model) c.Assert(err, jc.ErrorIsNil) params.ModelUUID = modelDetails.ModelUUID } return juju.NewAPIConnection(params) }
// apiInfoConnect looks for endpoint on the given environment and // tries to connect to it, sending the result on the returned channel. func apiInfoConnect(info configstore.EnvironInfo, apiOpen apiOpenFunc, stop <-chan struct{}) (apiState, error) { endpoint := info.APIEndpoint() if info == nil || len(endpoint.Addresses) == 0 { return nil, &infoConnectError{fmt.Errorf("no cached addresses")} } logger.Infof("connecting to API addresses: %v", endpoint.Addresses) var environTag names.EnvironTag if names.IsValidEnvironment(endpoint.EnvironUUID) { environTag = names.NewEnvironTag(endpoint.EnvironUUID) } apiInfo := &api.Info{ Addrs: endpoint.Addresses, CACert: endpoint.CACert, Tag: environInfoUserTag(info), Password: info.APICredentials().Password, EnvironTag: environTag, } st, err := apiOpen(apiInfo, api.DefaultDialOpts()) if err != nil { return nil, &infoConnectError{err} } return st, nil }
func checkCommonAPIInfoAttrs(c *gc.C, apiInfo *api.Info, opts api.DialOpts) { c.Check(apiInfo.Tag, gc.Equals, names.NewUserTag("foo")) c.Check(string(apiInfo.CACert), gc.Equals, "certificated") c.Check(apiInfo.Password, gc.Equals, "foopass") c.Check(opts, gc.DeepEquals, api.DefaultDialOpts()) }