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