github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/juju/commands/switch.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package commands
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/errors"
    12  
    13  	"github.com/juju/juju/cmd/modelcmd"
    14  	"github.com/juju/juju/juju/osenv"
    15  	"github.com/juju/juju/jujuclient"
    16  )
    17  
    18  func newSwitchCommand() cmd.Command {
    19  	cmd := &switchCommand{
    20  		Store: jujuclient.NewFileClientStore(),
    21  	}
    22  	cmd.RefreshModels = cmd.JujuCommandBase.RefreshModels
    23  	return modelcmd.WrapBase(cmd)
    24  }
    25  
    26  type switchCommand struct {
    27  	modelcmd.JujuCommandBase
    28  	RefreshModels func(jujuclient.ClientStore, string) error
    29  
    30  	Store  jujuclient.ClientStore
    31  	Target string
    32  }
    33  
    34  var usageSummary = `
    35  Selects or identifies the current controller and model.`[1:]
    36  
    37  var usageDetails = `
    38  When used without an argument, the command shows the current controller 
    39  and its active model. 
    40  When switching by controller name alone, the model
    41  you get is the active model for that controller. If you want a different
    42  model then you must switch using controller:model notation or switch to 
    43  the controller and then to the model. 
    44  The `[1:] + "`juju models`" + ` command can be used to determine the active model
    45  (of any controller). An asterisk denotes it.
    46  
    47  Examples:
    48      juju switch
    49      juju switch mymodel
    50      juju switch mycontroller
    51      juju switch mycontroller:mymodel
    52  
    53  See also: 
    54      controllers
    55      models
    56      show-controller`
    57  
    58  func (c *switchCommand) Info() *cmd.Info {
    59  	return &cmd.Info{
    60  		Name:    "switch",
    61  		Args:    "[<controller>|<model>|<controller>:<model>]",
    62  		Purpose: usageSummary,
    63  		Doc:     usageDetails,
    64  	}
    65  }
    66  
    67  func (c *switchCommand) Init(args []string) error {
    68  	var err error
    69  	c.Target, err = cmd.ZeroOrOneArgs(args)
    70  	return err
    71  }
    72  
    73  func (c *switchCommand) Run(ctx *cmd.Context) (resultErr error) {
    74  	store := modelcmd.QualifyingClientStore{c.Store}
    75  
    76  	// Get the current name for logging the transition or printing
    77  	// the current controller/model.
    78  	currentControllerName, err := store.CurrentController()
    79  	if errors.IsNotFound(err) {
    80  		currentControllerName = ""
    81  	} else if err != nil {
    82  		return errors.Trace(err)
    83  	}
    84  	if c.Target == "" {
    85  		currentName, err := c.name(store, currentControllerName, true)
    86  		if err != nil {
    87  			return errors.Trace(err)
    88  		}
    89  		if currentName == "" {
    90  			return errors.New("no currently specified model")
    91  		}
    92  		fmt.Fprintf(ctx.Stdout, "%s\n", currentName)
    93  		return nil
    94  	}
    95  	currentName, err := c.name(store, currentControllerName, false)
    96  	if err != nil {
    97  		return errors.Trace(err)
    98  	}
    99  
   100  	var newName string
   101  	defer func() {
   102  		if resultErr != nil {
   103  			return
   104  		}
   105  		logSwitch(ctx, currentName, &newName)
   106  	}()
   107  
   108  	// Switch is an alternative way of dealing with environments than using
   109  	// the JUJU_MODEL environment setting, and as such, doesn't play too well.
   110  	// If JUJU_MODEL is set we should report that as the current environment,
   111  	// and not allow switching when it is set.
   112  	if model := os.Getenv(osenv.JujuModelEnvKey); model != "" {
   113  		return errors.Errorf("cannot switch when JUJU_MODEL is overriding the model (set to %q)", model)
   114  	}
   115  
   116  	// If the target identifies a controller, then set that as the current controller.
   117  	var newControllerName = c.Target
   118  	if _, err = store.ControllerByName(c.Target); err == nil {
   119  		if newControllerName == currentControllerName {
   120  			newName = currentName
   121  			return nil
   122  		} else {
   123  			newName, err = c.name(store, newControllerName, false)
   124  			if err != nil {
   125  				return errors.Trace(err)
   126  			}
   127  			return errors.Trace(store.SetCurrentController(newControllerName))
   128  		}
   129  	} else if !errors.IsNotFound(err) {
   130  		return errors.Trace(err)
   131  	}
   132  
   133  	// The target is not a controller, so check for a model with
   134  	// the given name. The name can be qualified with the controller
   135  	// name (<controller>:<model>), or unqualified; in the latter
   136  	// case, the model must exist in the current controller.
   137  	newControllerName, modelName := modelcmd.SplitModelName(c.Target)
   138  	if newControllerName != "" {
   139  		if _, err = store.ControllerByName(newControllerName); err != nil {
   140  			return errors.Trace(err)
   141  		}
   142  	} else {
   143  		if currentControllerName == "" {
   144  			return unknownSwitchTargetError(c.Target)
   145  		}
   146  		newControllerName = currentControllerName
   147  	}
   148  	modelName, err = store.QualifiedModelName(newControllerName, modelName)
   149  	if err != nil {
   150  		return errors.Trace(err)
   151  	}
   152  	newName = modelcmd.JoinModelName(newControllerName, modelName)
   153  
   154  	err = store.SetCurrentModel(newControllerName, modelName)
   155  	if errors.IsNotFound(err) {
   156  		// The model isn't known locally, so we must query the controller.
   157  		if err := c.RefreshModels(store, newControllerName); err != nil {
   158  			return errors.Annotate(err, "refreshing models cache")
   159  		}
   160  		err := store.SetCurrentModel(newControllerName, modelName)
   161  		if errors.IsNotFound(err) {
   162  			return unknownSwitchTargetError(c.Target)
   163  		} else if err != nil {
   164  			return errors.Trace(err)
   165  		}
   166  	} else if err != nil {
   167  		return errors.Trace(err)
   168  	}
   169  	if currentControllerName != newControllerName {
   170  		if err := store.SetCurrentController(newControllerName); err != nil {
   171  			return errors.Trace(err)
   172  		}
   173  	}
   174  	return nil
   175  }
   176  
   177  func unknownSwitchTargetError(name string) error {
   178  	return errors.Errorf("%q is not the name of a model or controller", name)
   179  }
   180  
   181  func logSwitch(ctx *cmd.Context, oldName string, newName *string) {
   182  	if *newName == oldName {
   183  		ctx.Infof("%s (no change)", oldName)
   184  	} else {
   185  		ctx.Infof("%s -> %s", oldName, *newName)
   186  	}
   187  }
   188  
   189  // name returns the name of the current model for the specified controller
   190  // if one is set, otherwise the controller name with an indicator that it
   191  // is the name of a controller and not a model.
   192  func (c *switchCommand) name(store jujuclient.ModelGetter, controllerName string, machineReadable bool) (string, error) {
   193  	if controllerName == "" {
   194  		return "", nil
   195  	}
   196  	modelName, err := store.CurrentModel(controllerName)
   197  	if err == nil {
   198  		return modelcmd.JoinModelName(controllerName, modelName), nil
   199  	}
   200  	if !errors.IsNotFound(err) {
   201  		return "", errors.Trace(err)
   202  	}
   203  	// No current account or model.
   204  	if machineReadable {
   205  		return controllerName, nil
   206  	}
   207  	return fmt.Sprintf("%s (controller)", controllerName), nil
   208  }