func (s *cmdControllerSuite) TestControllerLoginCommand(c *gc.C) {
	user := s.Factory.MakeUser(c, &factory.UserParams{
		NoEnvUser: true,
		Password:  "******",
	})
	apiInfo := s.APIInfo(c)
	serverFile := envcmd.ServerFile{
		Addresses: apiInfo.Addrs,
		CACert:    apiInfo.CACert,
		Username:  user.Name(),
		Password:  "******",
	}
	serverFilePath := filepath.Join(c.MkDir(), "server.yaml")
	content, err := goyaml.Marshal(serverFile)
	c.Assert(err, jc.ErrorIsNil)
	err = ioutil.WriteFile(serverFilePath, []byte(content), 0644)
	c.Assert(err, jc.ErrorIsNil)

	s.run(c, "login", "--server", serverFilePath, "just-a-controller")

	// Make sure that the saved server details are sufficient to connect
	// to the api server.
	api, err := juju.NewAPIFromName("just-a-controller", nil)
	c.Assert(err, jc.ErrorIsNil)
	api.Close()
}
Beispiel #2
0
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()
}
Beispiel #3
0
// RunPost sends credentials obtained during the call to RunPre to the controller.
func (r *RegisterMeteredCharm) RunPost(state api.Connection, bakeryClient *httpbakery.Client, ctx *cmd.Context, deployInfo DeploymentInfo, prevErr error) error {
	if prevErr != nil {
		return nil
	}
	if r.credentials == nil {
		return nil
	}
	api, cerr := getMetricCredentialsAPI(state)
	if cerr != nil {
		logger.Infof("failed to get the metrics credentials setter: %v", cerr)
		return cerr
	}
	defer api.Close()

	err := api.SetMetricCredentials(deployInfo.ServiceName, r.credentials)
	if err != nil {
		logger.Warningf("failed to set metric credentials: %v", err)
		return errors.Trace(err)
	}

	err = r.AllocateBudget.RunPost(state, bakeryClient, ctx, deployInfo, prevErr)
	if err != nil {
		logger.Warningf("failed to allocate budget: %v", err)
		return errors.Trace(err)
	}

	return nil
}
Beispiel #4
0
func registerMeteredCharm(registrationURL string, state *api.State, jar *cookiejar.Jar, charmURL string, serviceName, environmentUUID string) error {
	charmsClient := charms.NewClient(state)
	defer charmsClient.Close()
	metered, err := charmsClient.IsMetered(charmURL)
	if err != nil {
		return err
	}
	if metered {
		httpClient := httpbakery.NewHTTPClient()
		httpClient.Jar = jar
		credentials, err := registerMetrics(registrationURL, environmentUUID, charmURL, serviceName, httpClient, openWebBrowser)
		if err != nil {
			logger.Infof("failed to register metrics: %v", err)
			return err
		}

		api, cerr := getMetricCredentialsAPI(state)
		if cerr != nil {
			logger.Infof("failed to get the metrics credentials setter: %v", cerr)
		}
		err = api.SetMetricCredentials(serviceName, credentials)
		if err != nil {
			logger.Infof("failed to set metric credentials: %v", err)
			return err
		}
		api.Close()
	}
	return nil
}
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()
}
Beispiel #6
0
func (c *SpaceCommandBase) RunWithAPI(ctx *cmd.Context, toRun RunOnAPI) error {
	api, err := c.NewAPI()
	if err != nil {
		return errors.Annotate(err, "cannot connect to the API server")
	}
	defer api.Close()
	return toRun(api, ctx)
}
Beispiel #7
0
func (c *registerCommand) listModels(store jujuclient.ClientStore, controllerName, userName string) ([]base.UserModel, error) {
	api, err := c.NewAPIRoot(store, controllerName, "")
	if err != nil {
		return nil, errors.Trace(err)
	}
	defer api.Close()
	mm := modelmanager.NewClient(api)
	return mm.ListModels(userName)
}
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)
}
Beispiel #9
0
// killSystemViaClient attempts to kill the system using the client
// endpoint for older juju systems which do not implement systemmanager.DestroySystem
func (c *killCommand) killSystemViaClient(ctx *cmd.Context, info configstore.EnvironInfo, systemEnviron environs.Environ, store configstore.Storage) error {
	api, err := c.getClientAPI()
	if err != nil {
		defer api.Close()
	}

	if api != nil {
		err = api.DestroyEnvironment()
		if err != nil {
			ctx.Infof("Unable to destroy system through the API: %s.  Destroying through provider.", err)
		}
	}

	return environs.Destroy(systemEnviron, store)
}
Beispiel #10
0
Datei: list.go Projekt: bac/juju
func (c *listCommand) listForModel(ctx *cmd.Context) (err error) {
	api, err := c.apiFunc(c)
	if err != nil {
		return errors.Trace(err)
	}
	defer api.Close()

	result, err := api.List()
	if err != nil {
		return errors.Trace(err)
	}
	if len(result) == 0 && c.out.Name() == "tabular" {
		ctx.Infof(noBlocks)
		return nil
	}
	return c.out.Write(ctx, formatBlockInfo(result))
}
func (s *cmdControllerSuite) TestCreateEnvironment(c *gc.C) {
	c.Assert(envcmd.WriteCurrentController("dummyenv"), jc.ErrorIsNil)
	// 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 'state-server'.
	context := s.run(c, "create-environment", "new-env", "authorized-keys=fake-key", "state-server=false")
	c.Check(testing.Stdout(context), gc.Equals, "")
	c.Check(testing.Stderr(context), gc.Equals, `
created environment "new-env"
dummyenv (controller) -> new-env
`[1:])

	// Make sure that the saved server details are sufficient to connect
	// to the api server.
	api, err := juju.NewAPIFromName("new-env", nil)
	c.Assert(err, jc.ErrorIsNil)
	api.Close()
}
Beispiel #12
0
func (c *migrateCommand) getTargetControllerMacaroons() ([]macaroon.Slice, error) {
	apiContext, err := c.APIContext()
	if err != nil {
		return nil, errors.Trace(err)
	}

	// Connect to the target controller, ensuring up-to-date macaroons,
	// and return the macaroons in the cookie jar for the controller.
	//
	// TODO(axw,mjs) add a controller API that returns a macaroon that
	// may be used for the sole purpose of migration.
	api, err := c.newAPIRoot(c.ClientStore(), c.targetController, "")
	if err != nil {
		return nil, errors.Annotate(err, "connecting to target controller")
	}
	defer api.Close()
	return httpbakery.MacaroonsForURL(apiContext.Jar, api.CookieURL()), nil
}
Beispiel #13
0
func (t *timeoutOpener) Open(store jujuclient.ClientStore, controllerName, modelName string) (api.Connection, error) {
	// Make the channels buffered so the created goroutine is guaranteed
	// not go get blocked trying to send down the channel.
	apic := make(chan api.Connection, 1)
	errc := make(chan error, 1)
	go func() {
		api, dialErr := t.opener.Open(store, controllerName, modelName)
		if dialErr != nil {
			errc <- dialErr
			return
		}

		select {
		case apic <- api:
			// sent fine
		default:
			// couldn't send, was blocked by the dummy value, must have timed out.
			api.Close()
		}
	}()

	var apiRoot api.Connection
	select {
	case err := <-errc:
		return nil, err
	case apiRoot = <-apic:
	case <-t.clock.After(t.timeout):
		select {
		case apic <- nil:
			// Fill up the buffer on the apic to indicate to the other goroutine
			// that we have timed out.
		case apiRoot = <-apic:
			// We hit that weird edge case where we have both timed out and
			// returned a viable apiRoot at exactly the same time, and the other
			// goroutine managed to send back the apiRoot before we pushed the
			// dummy value.  If this is the case, then we are good, return the
			// apiRoot
			return apiRoot, nil
		}
		return nil, ErrConnTimedOut
	}

	return apiRoot, nil
}
Beispiel #14
0
// RunPost sends credentials obtained during the call to RunPre to the controller.
func (r *RegisterMeteredCharm) RunPost(state api.Connection, client *http.Client, deployInfo DeploymentInfo) error {
	if r.credentials == nil {
		return nil
	}
	api, cerr := getMetricCredentialsAPI(state)
	if cerr != nil {
		logger.Infof("failed to get the metrics credentials setter: %v", cerr)
		return cerr
	}
	defer api.Close()

	err := api.SetMetricCredentials(deployInfo.ServiceName, r.credentials)
	if err != nil {
		logger.Infof("failed to set metric credentials: %v", err)
		return err
	}

	return nil
}
Beispiel #15
0
func (c *changePasswordCommand) recordMacaroon(user, password string) error {
	accountDetails := &jujuclient.AccountDetails{User: user}
	args, err := c.NewAPIConnectionParams(
		c.ClientStore(), c.ControllerName(), "", accountDetails,
	)
	if err != nil {
		return errors.Trace(err)
	}
	args.DialOpts.BakeryClient.WebPageVisitor = httpbakery.NewMultiVisitor(
		authentication.NewVisitor(accountDetails.User, func(string) (string, error) {
			return password, nil
		}),
		args.DialOpts.BakeryClient.WebPageVisitor,
	)
	api, err := c.newAPIConnection(args)
	if err != nil {
		return errors.Annotate(err, "connecting to API")
	}
	return api.Close()
}
Beispiel #16
0
Datei: list.go Projekt: bac/juju
func (c *listCommand) listForController(ctx *cmd.Context) (err error) {
	api, err := c.controllerAPIFunc(c)
	if err != nil {
		return errors.Trace(err)
	}
	defer api.Close()

	result, err := api.ListBlockedModels()
	if err != nil {
		return errors.Trace(err)
	}
	if len(result) == 0 && c.out.Name() == "tabular" {
		ctx.Infof(noBlocks)
		return nil
	}
	info, err := FormatModelBlockInfo(result)
	if err != nil {
		return errors.Trace(err)
	}
	return c.out.Write(ctx, info)
}
Beispiel #17
0
// RunPost sends credentials obtained during the call to RunPre to the controller.
func (r *RegisterMeteredCharm) RunPost(state api.Connection, client *http.Client, deployInfo DeploymentInfo) error {
	if r.credentials == nil {
		return nil
	}
	api, cerr := getMetricCredentialsAPI(state)
	if cerr != nil {
		logger.Infof("failed to get the metrics credentials setter: %v", cerr)
		return cerr
	}
	defer api.Close()

	err := api.SetMetricCredentials(deployInfo.ServiceName, r.credentials)
	if params.IsCodeNotImplemented(err) {
		// The state server is too old to support metering.  Warn
		// the user, but don't return an error.
		logger.Warningf("current state server version does not support charm metering")
		return nil
	} else if err != nil {
		logger.Infof("failed to set metric credentials: %v", err)
		return err
	}

	return nil
}
Beispiel #18
0
		credentials, err := registerMetrics(registrationURL, environmentUUID, charmURL, serviceName, httpClient, openWebBrowser)
		if err != nil {
			logger.Infof("failed to register metrics: %v", err)
			return err
		}

		api, cerr := getMetricCredentialsAPI(state)
		if cerr != nil {
			logger.Infof("failed to get the metrics credentials setter: %v", cerr)
		}
		err = api.SetMetricCredentials(serviceName, credentials)
		if err != nil {
			logger.Infof("failed to set metric credentials: %v", err)
			return err
		}
		api.Close()
	}
	return nil
}

func registerMetrics(registrationURL, environmentUUID, charmURL, serviceName string, client *http.Client, visitWebPage func(*url.URL) error) ([]byte, error) {
	if registrationURL == "" {
		return nil, errors.Errorf("no metric registration url is specified")
	}
	registerURL, err := url.Parse(registrationURL)
	if err != nil {
		return nil, errors.Trace(err)
	}

	registrationPost := metricRegistrationPost{
		EnvironmentUUID: environmentUUID,
Beispiel #19
0
func (s *cmdRegistrationSuite) TestAddUserAndRegister(c *gc.C) {
	// First, add user "bob", and record the "juju register" command
	// that is printed out.

	context := run(c, nil, "add-user", "bob", "Bob Dobbs")
	c.Check(testing.Stderr(context), gc.Equals, "")
	stdout := testing.Stdout(context)
	expectPat := `
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:]
	c.Assert(stdout, gc.Matches, expectPat)

	arg := regexp.MustCompile("^" + expectPat + "$").FindStringSubmatch(stdout)[1]
	c.Logf("juju register %q", arg)

	// Now run the "juju register" command. We need to pass the
	// controller name and password to set, and we need a different
	// file store to mimic a different local OS user.
	s.CreateUserHome(c, &jujutesting.UserHomeParams{
		Username: "******",
	})

	// The expected prompt does not include a warning about the controller
	// name, as this new local user does not have a controller named
	// "kontroll" registered.
	prompter := cmdtesting.NewSeqPrompter(c, "»", `
Enter a new password: »hunter2

Confirm password: »hunter2

Initial password successfully set for bob.
Enter a name for this controller \[kontroll\]: »bob-controller

Welcome, bob. You are now logged into "bob-controller".

There are no models available. (.|\n)*
`[1:])

	context = run(c, prompter, "register", arg)
	prompter.AssertDone()

	// Make sure that the saved server details are sufficient to connect
	// to the api server.
	jar, err := cookiejar.New(&cookiejar.Options{
		Filename: cookiejar.DefaultCookieFile(),
	})
	c.Assert(err, jc.ErrorIsNil)
	dialOpts := api.DefaultDialOpts()
	dialOpts.BakeryClient = httpbakery.NewClient()
	dialOpts.BakeryClient.Jar = jar
	accountDetails, err := s.ControllerStore.AccountDetails("bob-controller")
	c.Assert(err, jc.ErrorIsNil)
	api, err := juju.NewAPIConnection(juju.NewAPIConnectionParams{
		Store:          s.ControllerStore,
		ControllerName: "bob-controller",
		AccountDetails: accountDetails,
		DialOpts:       dialOpts,
		OpenAPI:        api.Open,
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(api.Close(), jc.ErrorIsNil)
}
Beispiel #20
0
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, and we need a different
	// file store to mimic a different local OS user.
	userHomeParams := jujutesting.UserHomeParams{Username: "******"}
	s.CreateUserHome(c, &userHomeParams)
	stdin := strings.NewReader("bob-controller\nhunter2\nhunter2\n")
	args := jujuRegisterCommand[1:] // drop the "juju"

	// The expected prompt does not include a warning about the controller
	// name, as this new local user does not have a controller named
	// "kontroll" registered.
	expectedPrompt := `
Enter a name for this controller [kontroll]: 
Enter a new password: 
Confirm password: 

Welcome, bob. You are now logged into "bob-controller".

There are no models available. You can add models with
"juju add-model", or you can ask an administrator or owner
of a model to grant access to that model with "juju grant".

`[1:]

	context = s.run(c, stdin, args...)
	c.Check(testing.Stdout(context), gc.Equals, "")
	c.Check(testing.Stderr(context), gc.Equals, expectedPrompt)

	// Make sure that the saved server details are sufficient to connect
	// to the api server.
	jar, err := cookiejar.New(&cookiejar.Options{
		Filename: cookiejar.DefaultCookieFile(),
	})
	c.Assert(err, jc.ErrorIsNil)
	dialOpts := api.DefaultDialOpts()
	dialOpts.BakeryClient = httpbakery.NewClient()
	dialOpts.BakeryClient.Jar = jar
	accountDetails, err := s.ControllerStore.AccountDetails("bob-controller")
	c.Assert(err, jc.ErrorIsNil)
	api, err := juju.NewAPIConnection(juju.NewAPIConnectionParams{
		Store:          s.ControllerStore,
		ControllerName: "bob-controller",
		AccountDetails: accountDetails,
		DialOpts:       dialOpts,
		OpenAPI:        api.Open,
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(api.Close(), jc.ErrorIsNil)
}
Beispiel #21
0
// Run implements Command.Run
func (c *killCommand) Run(ctx *cmd.Context) error {
	store, err := configstore.Default()
	if err != nil {
		return errors.Annotate(err, "cannot open controller info storage")
	}

	cfgInfo, err := store.ReadInfo(c.ModelName())
	if err != nil {
		return errors.Annotate(err, "cannot read controller info")
	}

	// Verify that we're destroying a controller
	apiEndpoint := cfgInfo.APIEndpoint()
	if apiEndpoint.ServerUUID != "" && apiEndpoint.ModelUUID != apiEndpoint.ServerUUID {
		return errors.Errorf("%q is not a controller; use juju model destroy to destroy it", c.ModelName())
	}

	if !c.assumeYes {
		if err = confirmDestruction(ctx, c.ModelName()); err != nil {
			return err
		}
	}

	// Attempt to connect to the API.
	api, err := c.getControllerAPI()
	switch {
	case err == nil:
		defer api.Close()
	case errors.Cause(err) == common.ErrPerm:
		return errors.Annotate(err, "cannot destroy controller")
	default:
		if errors.Cause(err) != modelcmd.ErrConnTimedOut {
			logger.Debugf("unable to open api: %s", err)
		}
		ctx.Infof("Unable to open API: %s\n", err)
		api = nil
	}

	// Obtain bootstrap / controller environ information
	controllerEnviron, err := c.getControllerEnviron(cfgInfo, api)
	if err != nil {
		return errors.Annotate(err, "cannot obtain bootstrap information")
	}

	// If we were unable to connect to the API, just destroy the controller through
	// the environs interface.
	if api == nil {
		ctx.Infof("Unable to connect to the API server. Destroying through provider.")
		return environs.Destroy(controllerEnviron, store)
	}

	// Attempt to destroy the controller and all environments.
	err = api.DestroyController(true)
	if err != nil {
		ctx.Infof("Unable to destroy controller through the API: %s.  Destroying through provider.", err)
		return environs.Destroy(controllerEnviron, store)
	}

	ctx.Infof("Destroying controller %q\nWaiting for resources to be reclaimed", c.ModelName())

	updateStatus := newTimedStatusUpdater(ctx, api, apiEndpoint.ModelUUID)
	for ctrStatus, envsStatus := updateStatus(0); hasUnDeadEnvirons(envsStatus); ctrStatus, envsStatus = updateStatus(2 * time.Second) {
		ctx.Infof(fmtCtrStatus(ctrStatus))
		for _, envStatus := range envsStatus {
			ctx.Verbosef(fmtEnvStatus(envStatus))
		}
	}

	ctx.Infof("All hosted models reclaimed, cleaning up controller machines")

	return environs.Destroy(controllerEnviron, store)
}
Beispiel #22
0
Datei: main.go Projekt: axw/jns
// TODO(axw) define an error for "no answer"
func (s *jujuNameServer) answer(q dns.Question) (dns.RR, error) {
	if q.Qtype != dns.TypeA {
		return nil, nil
	}

	var envName string
	entityName := strings.ToLower(strings.TrimSuffix(q.Name, "."+zone))
	if i := strings.IndexRune(entityName, '.'); i >= 0 {
		envName = entityName[i+1:]
		entityName = entityName[:i]
	} else {
		var err error
		envName, err = envcmd.GetDefaultEnvironment()
		if err != nil {
			return nil, err
		}
	}

	// TODO(axw) cache API connection
	api, err := s.openAPI(envName)
	if err != nil {
		return nil, err
	}
	defer api.Close()
	client := api.Client()

	// If the entity name parses as a tag, extract the ID. This enables
	// us to address "unit-mysql-0", where we couldn't otherwise, since
	// slashes are not allowed in domain names. Similarly for container
	// machines (e.g. to address "0/lxc/0", pass "machine-0-lxc-0").
	if tag, err := names.ParseTag(entityName); err == nil {
		entityName = tag.Id()
	}

	var addr string
	if names.IsValidService(entityName) {
		status, err := client.Status([]string{entityName})
		if err != nil {
			return nil, err
		}
		service := status.Services[entityName]
		addresses := make([]string, 0, len(service.Units))
		for _, unit := range service.Units {
			if unit.PublicAddress == "" {
				continue
			}
			if includeLost || unit.UnitAgent.Status != "lost" {
				addresses = append(addresses, unit.PublicAddress)
			}
		}
		// Might be nice to have additional info in TXT?
		if len(addresses) == 0 {
			return nil, nil
		}
		addr = addresses[rand.Intn(len(addresses))]
	} else {
		// Assume it's a machine or unit name.
		addr, err = client.PublicAddress(entityName)
		if err != nil {
			return nil, err
		}
	}

	ip := net.ParseIP(addr)
	if ip != nil {
		rr := new(dns.A)
		rr.Hdr = dns.RR_Header{
			Name:   q.Name,
			Rrtype: dns.TypeA,
			Class:  dns.ClassINET,
			Ttl:    0,
		}
		rr.A = ip
		return rr, nil
	} else {
		rr := new(dns.CNAME)
		rr.Hdr = dns.RR_Header{
			Name:   q.Name,
			Rrtype: dns.TypeCNAME,
			Class:  dns.ClassINET,
			Ttl:    0,
		}
		rr.Target = addr + "."
		return rr, nil
	}
}
Beispiel #23
0
func (c *addModelCommand) Run(ctx *cmd.Context) error {
	api, err := c.newApiRoot()
	if err != nil {
		return errors.Annotate(err, "opening API connection")
	}
	defer api.Close()

	store := c.ClientStore()
	controllerName := c.ControllerName()
	accountDetails, err := store.AccountDetails(controllerName)
	if err != nil {
		return errors.Trace(err)
	}

	modelOwner := accountDetails.User
	if c.Owner != "" {
		if !names.IsValidUser(c.Owner) {
			return errors.Errorf("%q is not a valid user name", c.Owner)
		}
		modelOwner = names.NewUserTag(c.Owner).Canonical()
	}
	forUserSuffix := fmt.Sprintf(" for user '%s'", names.NewUserTag(modelOwner).Name())

	attrs, err := c.getConfigValues(ctx)
	if err != nil {
		return errors.Trace(err)
	}

	cloudClient := c.newCloudAPI(api)
	var cloudTag names.CloudTag
	var cloud jujucloud.Cloud
	var cloudRegion string
	if c.CloudRegion != "" {
		cloudTag, cloud, cloudRegion, err = c.getCloudRegion(cloudClient)
		if err != nil {
			return errors.Trace(err)
		}
	}

	// If the user has specified a credential, then we will upload it if
	// it doesn't already exist in the controller, and it exists locally.
	var credentialTag names.CloudCredentialTag
	if c.CredentialName != "" {
		var err error
		if c.CloudRegion == "" {
			if cloudTag, cloud, err = defaultCloud(cloudClient); err != nil {
				return errors.Trace(err)
			}
		}
		credentialTag, err = c.maybeUploadCredential(ctx, cloudClient, cloudTag, cloudRegion, cloud, modelOwner)
		if err != nil {
			return errors.Trace(err)
		}
	}

	addModelClient := c.newAddModelAPI(api)
	model, err := addModelClient.CreateModel(c.Name, modelOwner, cloudTag.Id(), cloudRegion, credentialTag, attrs)
	if err != nil {
		return errors.Trace(err)
	}

	messageFormat := "Added '%s' model"
	messageArgs := []interface{}{c.Name}

	if modelOwner == accountDetails.User {
		controllerName := c.ControllerName()
		if err := store.UpdateModel(controllerName, c.Name, jujuclient.ModelDetails{
			model.UUID,
		}); err != nil {
			return errors.Trace(err)
		}
		if err := store.SetCurrentModel(controllerName, c.Name); err != nil {
			return errors.Trace(err)
		}
	}

	if c.CloudRegion != "" || model.CloudRegion != "" {
		// The user explicitly requested a cloud/region,
		// or the cloud supports multiple regions. Whichever
		// the case, tell the user which cloud/region the
		// model was deployed to.
		cloudRegion := model.Cloud
		if model.CloudRegion != "" {
			cloudRegion += "/" + model.CloudRegion
		}
		messageFormat += " on %s"
		messageArgs = append(messageArgs, cloudRegion)
	}
	if model.CloudCredential != "" {
		tag := names.NewCloudCredentialTag(model.CloudCredential)
		credentialName := tag.Name()
		if tag.Owner().Canonical() != modelOwner {
			credentialName = fmt.Sprintf("%s/%s", tag.Owner().Canonical(), credentialName)
		}
		messageFormat += " with credential '%s'"
		messageArgs = append(messageArgs, credentialName)
	}

	messageFormat += forUserSuffix

	// "Added '<model>' model [on <cloud>/<region>] [with credential '<credential>'] for user '<user namePart>'"
	ctx.Infof(messageFormat, messageArgs...)

	if _, ok := attrs[config.AuthorizedKeysKey]; !ok {
		// It is not an error to have no authorized-keys when adding a
		// model, though this should never happen since we generate
		// juju-specific SSH keys.
		ctx.Infof(`
No SSH authorized-keys were found. You must use "juju add-ssh-key"
before "juju ssh", "juju scp", or "juju debug-hooks" will work.`)
	}

	return nil
}
Beispiel #24
0
// Run implements Command.Run
func (c *killCommand) Run(ctx *cmd.Context) error {
	if c.apiDialerFunc == nil {
		c.apiDialerFunc = c.NewAPIRoot
	}

	store, err := configstore.Default()
	if err != nil {
		return errors.Annotate(err, "cannot open system info storage")
	}

	cfgInfo, err := store.ReadInfo(c.systemName)
	if err != nil {
		return errors.Annotate(err, "cannot read system info")
	}

	// Verify that we're destroying a system
	apiEndpoint := cfgInfo.APIEndpoint()
	if apiEndpoint.ServerUUID != "" && apiEndpoint.EnvironUUID != apiEndpoint.ServerUUID {
		return errors.Errorf("%q is not a system; use juju environment destroy to destroy it", c.systemName)
	}

	if !c.assumeYes {
		if err = confirmDestruction(ctx, c.systemName); err != nil {
			return err
		}
	}

	// Attempt to connect to the API.
	api, err := c.getSystemAPI(cfgInfo)
	switch {
	case err == nil:
		defer api.Close()
	case errors.Cause(err) == common.ErrPerm:
		return errors.Annotate(err, "cannot destroy system")
	default:
		if err != ErrConnTimedOut {
			logger.Debugf("unable to open api: %s", err)
		}
		ctx.Infof("Unable to open API: %s\n", err)
		api = nil
	}

	// Obtain bootstrap / system environ information
	systemEnviron, err := c.getSystemEnviron(cfgInfo, api)
	if err != nil {
		return errors.Annotate(err, "cannot obtain bootstrap information")
	}

	// If we were unable to connect to the API, just destroy the system through
	// the environs interface.
	if api == nil {
		return environs.Destroy(systemEnviron, store)
	}

	// Attempt to destroy the system with destroyEnvs and ignoreBlocks = true
	err = api.DestroySystem(true, true)
	if params.IsCodeNotImplemented(err) {
		// Fall back to using the client endpoint to destroy the system,
		// sending the info we were already able to collect.
		return c.killSystemViaClient(ctx, cfgInfo, systemEnviron, store)
	}

	if err != nil {
		ctx.Infof("Unable to destroy system through the API: %s.  Destroying through provider.", err)
	}

	return environs.Destroy(systemEnviron, store)
}