// 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) } apiInfo := &api.Info{ Addrs: endpoint.Addresses, CACert: endpoint.CACert, Tag: names.NewUserTag(info.APICredentials().User), Password: info.APICredentials().Password, EnvironTag: environTag, } st, err := apiOpen(apiInfo, api.DefaultDialOpts()) if err != nil { return nil, &infoConnectError{err} } return st, nil }
func (*NewAPIConnSuite) TestNewConn(c *gc.C) { cfg, err := config.New(config.NoDefaults, dummy.SampleConfig()) c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, environs.BootstrapParams{}) c.Assert(err, gc.IsNil) cfg = env.Config() cfg, err = cfg.Apply(map[string]interface{}{ "secret": "fnord", }) c.Assert(err, gc.IsNil) err = env.SetConfig(cfg) c.Assert(err, gc.IsNil) conn, err := juju.NewAPIConn(env, api.DefaultDialOpts()) c.Assert(err, gc.IsNil) c.Assert(conn.Environ, gc.Equals, env) c.Assert(conn.State, gc.NotNil) // the secrets will not be updated, as they already exist attrs, err := conn.State.Client().EnvironmentGet() c.Assert(attrs["secret"], gc.Equals, "pork") c.Assert(conn.Close(), gc.IsNil) }
func (s *stateSuite) TestClientNoNeedToPing(c *gc.C) { s.PatchValue(apiserver.MaxClientPingInterval, time.Duration(0)) st, err := api.Open(s.APIInfo(c), api.DefaultDialOpts()) c.Assert(err, gc.IsNil) time.Sleep(coretesting.ShortWait) err = st.Ping() c.Assert(err, gc.IsNil) }
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) // Verify the cache is empty. info, err := store.ReadInfo("myenv") c.Assert(err, gc.IsNil) c.Assert(info, gc.NotNil) c.Assert(info.APIEndpoint(), jc.DeepEquals, configstore.APIEndpoint{}) c.Assert(info.APICredentials(), jc.DeepEquals, configstore.APICredentials{}) 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), "") // Old servers won't hand back EnvironTag, so it should stay empty in // the cache c.Check(ep.EnvironUUID, gc.Equals, "") creds := info.APICredentials() c.Check(creds.User, gc.Equals, "admin") c.Check(creds.Password, gc.Equals, "adminpass") }
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 DirectClientFactory(conf *config.Config) (*Client, error) { env, err := environs.New(conf) if err != nil { return nil, err } dialOpts := api.DefaultDialOpts() conn, err := juju.NewAPIConn(env, dialOpts) if err != nil { return nil, err } wrapper := &Client{} wrapper.client = conn.State.Client() wrapper.apiState = conn.State //defer apiclient.Close() return wrapper, err }
// 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) (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) 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 (s *APIEndpointForEnvSuite) TestAPIEndpointNotCached(c *gc.C) { coretesting.WriteEnvironments(c, coretesting.MultipleEnvConfig) store, err := configstore.Default() c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) env, err := environs.PrepareFromName("erewhemos", ctx, store) c.Assert(err, gc.IsNil) defer dummy.Reset() envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, environs.BootstrapParams{}) c.Assert(err, gc.IsNil) // Note: if we get Bootstrap to start caching the API endpoint // immediately, we'll still want to have this test for compatibility. // We can just write blank info instead of reading and checking it is empty. savedInfo, err := store.ReadInfo("erewhemos") c.Assert(err, gc.IsNil) // Ensure that the data isn't cached c.Check(savedInfo.APIEndpoint().Addresses, gc.HasLen, 0) called := 0 expectState := mockedAPIState(true, true) apiOpen := func(apiInfo *api.Info, opts api.DialOpts) (juju.APIState, error) { c.Check(apiInfo.Tag, gc.Equals, "user-admin") c.Check(string(apiInfo.CACert), gc.Equals, coretesting.CACert) c.Check(apiInfo.Password, gc.Equals, coretesting.DefaultMongoPassword) c.Check(opts, gc.DeepEquals, api.DefaultDialOpts()) // we didn't know about it when connecting c.Check(apiInfo.EnvironTag, gc.Equals, "") called++ return expectState, nil } endpoint, err := juju.APIEndpointInStore("erewhemos", false, store, apiOpen) c.Assert(err, gc.IsNil) c.Assert(called, gc.Equals, 1) c.Check(endpoint.Addresses, gc.DeepEquals, []string{"0.1.2.3:1234"}) c.Check(endpoint.EnvironUUID, gc.Equals, "fake-uuid") }
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()) }
func (t *LiveTests) TestBootstrapAndDeploy(c *gc.C) { if !t.CanOpenState || !t.HasProvisioner { c.Skip(fmt.Sprintf("skipping provisioner test, CanOpenState: %v, HasProvisioner: %v", t.CanOpenState, t.HasProvisioner)) } t.BootstrapOnce(c) // TODO(niemeyer): Stop growing this kitchen sink test and split it into proper parts. c.Logf("opening state") st := t.Env.(testing.GetStater).GetStateInAPIServer() c.Logf("opening API connection") apiState, err := juju.NewAPIState(t.Env, api.DefaultDialOpts()) c.Assert(err, gc.IsNil) defer apiState.Close() // Check that the agent version has made it through the // bootstrap process (it's optional in the config.Config) cfg, err := st.EnvironConfig() c.Assert(err, gc.IsNil) agentVersion, ok := cfg.AgentVersion() c.Check(ok, gc.Equals, true) c.Check(agentVersion, gc.Equals, version.Current.Number) // Check that the constraints have been set in the environment. cons, err := st.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(cons.String(), gc.Equals, "mem=2048M") // Wait for machine agent to come up on the bootstrap // machine and find the deployed series from that. m0, err := st.Machine("0") c.Assert(err, gc.IsNil) instId0, err := m0.InstanceId() c.Assert(err, gc.IsNil) // Check that the API connection is working. status, err := apiState.Client().Status(nil) c.Assert(err, gc.IsNil) c.Assert(status.Machines["0"].InstanceId, gc.Equals, string(instId0)) mw0 := newMachineToolWaiter(m0) defer mw0.Stop() // If the series has not been specified, we expect the most recent Ubuntu LTS release to be used. expectedVersion := version.Current expectedVersion.Series = config.LatestLtsSeries() mtools0 := waitAgentTools(c, mw0, expectedVersion) // Create a new service and deploy a unit of it. c.Logf("deploying service") repoDir := c.MkDir() url := charmtesting.Charms.ClonedURL(repoDir, mtools0.Version.Series, "dummy") sch, err := testing.PutCharm(st, url, &charm.LocalRepository{Path: repoDir}, false) c.Assert(err, gc.IsNil) svc, err := st.AddService("dummy", "user-admin", sch, nil) c.Assert(err, gc.IsNil) units, err := juju.AddUnits(st, svc, 1, "") c.Assert(err, gc.IsNil) unit := units[0] // Wait for the unit's machine and associated agent to come up // and announce itself. mid1, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) m1, err := st.Machine(mid1) c.Assert(err, gc.IsNil) mw1 := newMachineToolWaiter(m1) defer mw1.Stop() waitAgentTools(c, mw1, mtools0.Version) err = m1.Refresh() c.Assert(err, gc.IsNil) instId1, err := m1.InstanceId() c.Assert(err, gc.IsNil) uw := newUnitToolWaiter(unit) defer uw.Stop() utools := waitAgentTools(c, uw, expectedVersion) // Check that we can upgrade the environment. newVersion := utools.Version newVersion.Patch++ t.checkUpgrade(c, st, newVersion, mw0, mw1, uw) // BUG(niemeyer): Logic below is very much wrong. Must be: // // 1. EnsureDying on the unit and EnsureDying on the machine // 2. Unit dies by itself // 3. Machine removes dead unit // 4. Machine dies by itself // 5. Provisioner removes dead machine // // Now remove the unit and its assigned machine and // check that the PA removes it. c.Logf("removing unit") err = unit.Destroy() c.Assert(err, gc.IsNil) // Wait until unit is dead uwatch := unit.Watch() defer uwatch.Stop() for unit.Life() != state.Dead { c.Logf("waiting for unit change") <-uwatch.Changes() err := unit.Refresh() c.Logf("refreshed; err %v", err) if errors.IsNotFound(err) { c.Logf("unit has been removed") break } c.Assert(err, gc.IsNil) } for { c.Logf("destroying machine") err := m1.Destroy() if err == nil { break } c.Assert(err, gc.FitsTypeOf, &state.HasAssignedUnitsError{}) time.Sleep(5 * time.Second) err = m1.Refresh() if errors.IsNotFound(err) { break } c.Assert(err, gc.IsNil) } c.Logf("waiting for instance to be removed") t.assertStopInstance(c, t.Env, instId1) }
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 fmt.Errorf("cannot extract configuration from backup file: %v", err) } progress("extracted credentials from backup file") store, err := configstore.Default() if err != nil { return err } cfg, _, err := environs.ConfigForName(c.EnvName, store) if err != nil { return err } env, err := rebootstrap(cfg, ctx, c.Constraints) if err != nil { return fmt.Errorf("cannot re-bootstrap environment: %v", err) } progress("connecting to newly bootstrapped instance") var conn *juju.APIConn // 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(); { conn, err = juju.NewAPIConn(env, api.DefaultDialOpts()) if err == nil || errors.Cause(err).Error() != "EOF" { break } progress("bootstrapped instance not ready - attempting to redial") } if err != nil { return fmt.Errorf("cannot connect to bootstrap instance: %v", err) } progress("restoring bootstrap machine") newInstId, machine0Addr, err := restoreBootstrapMachine(conn, c.backupFile, agentConf) if err != nil { return fmt.Errorf("cannot restore bootstrap machine: %v", err) } progress("restored bootstrap machine") // Update the environ state to point to the new instance. if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{ StateInstances: []instance.Id{newInstId}, }); err != nil { return fmt.Errorf("cannot update environ bootstrap state storage: %v", err) } // Construct our own state info rather than using juju.NewConn so // that we can avoid storage eventual-consistency issues // (and it's faster too). caCert, ok := cfg.CACert() if !ok { return fmt.Errorf("configuration has no CA certificate") } progress("opening state") // We need to retry here to allow mongo to come up on the restored state server. // The connection might succeed due to the mongo dial retries but there may still // be a problem issuing database commands. var st *state.State for a := attempt.Start(); a.Next(); { st, err = state.Open(&state.Info{ Info: mongo.Info{ Addrs: []string{fmt.Sprintf("%s:%d", machine0Addr, cfg.StatePort())}, CACert: caCert, }, Tag: agentConf.Credentials.Tag, Password: agentConf.Credentials.Password, }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) if err == nil { break } progress("state server not ready - attempting to re-connect") } if err != nil { return fmt.Errorf("cannot open state: %v", err) } progress("updating all machines") if err := updateAllMachines(st, machine0Addr); err != nil { return fmt.Errorf("cannot update machines: %v", err) } return nil }
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 fmt.Errorf("cannot extract configuration from backup file: %v", err) } progress("extracted credentials from backup file") store, err := configstore.Default() if err != nil { return err } cfg, _, err := environs.ConfigForName(c.EnvName, store) if err != nil { return err } env, err := rebootstrap(cfg, ctx, c.Constraints) if err != nil { return fmt.Errorf("cannot re-bootstrap environment: %v", err) } progress("connecting to newly bootstrapped instance") conn, err := juju.NewAPIConn(env, api.DefaultDialOpts()) if err != nil { return fmt.Errorf("cannot connect to bootstrap instance: %v", err) } progress("restoring bootstrap machine") newInstId, machine0Addr, err := restoreBootstrapMachine(conn, c.backupFile, agentConf) if err != nil { return fmt.Errorf("cannot restore bootstrap machine: %v", err) } progress("restored bootstrap machine") // Update the environ state to point to the new instance. if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{ StateInstances: []instance.Id{newInstId}, }); err != nil { return fmt.Errorf("cannot update environ bootstrap state storage: %v", err) } // Construct our own state info rather than using juju.NewConn so // that we can avoid storage eventual-consistency issues // (and it's faster too). caCert, ok := cfg.CACert() if !ok { return fmt.Errorf("configuration has no CA certificate") } progress("opening state") st, err := state.Open(&state.Info{ Info: mongo.Info{ Addrs: []string{fmt.Sprintf("%s:%d", machine0Addr, cfg.StatePort())}, CACert: caCert, }, Tag: agentConf.Credentials.Tag, Password: agentConf.Credentials.Password, }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) if err != nil { return fmt.Errorf("cannot open state: %v", err) } progress("updating all machines") if err := updateAllMachines(st, machine0Addr); err != nil { return fmt.Errorf("cannot update machines: %v", err) } return nil }