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