github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  			newName = modelcmd.JoinModelName(newControllerName, modelName)
   144  		} else {
   145  			newName = c.Target
   146  		}
   147  	} else {
   148  		if currentControllerName == "" {
   149  			return unknownSwitchTargetError(c.Target)
   150  		}
   151  		newControllerName = currentControllerName
   152  		newName = modelcmd.JoinModelName(newControllerName, modelName)
   153  	}
   154  
   155  	accountName, err := c.Store.CurrentAccount(newControllerName)
   156  	if err != nil {
   157  		return errors.Trace(err)
   158  	}
   159  	err = c.Store.SetCurrentModel(newControllerName, accountName, modelName)
   160  	if errors.IsNotFound(err) {
   161  		// The model isn't known locally, so we must query the controller.
   162  		if err := c.RefreshModels(c.Store, newControllerName, accountName); err != nil {
   163  			return errors.Annotate(err, "refreshing models cache")
   164  		}
   165  		err := c.Store.SetCurrentModel(newControllerName, accountName, modelName)
   166  		if errors.IsNotFound(err) {
   167  			return unknownSwitchTargetError(c.Target)
   168  		} else if err != nil {
   169  			return errors.Trace(err)
   170  		}
   171  	} else if err != nil {
   172  		return errors.Trace(err)
   173  	}
   174  	if currentControllerName != newControllerName {
   175  		if err := c.WriteCurrentController(newControllerName); err != nil {
   176  			return errors.Trace(err)
   177  		}
   178  	}
   179  	return nil
   180  }
   181  
   182  func unknownSwitchTargetError(name string) error {
   183  	return errors.Errorf("%q is not the name of a model or controller", name)
   184  }
   185  
   186  func logSwitch(ctx *cmd.Context, oldName string, newName *string) {
   187  	if *newName == oldName {
   188  		ctx.Infof("%s (no change)", oldName)
   189  	} else {
   190  		ctx.Infof("%s -> %s", oldName, *newName)
   191  	}
   192  }
   193  
   194  // name returns the name of the current model for the specified controller
   195  // if one is set, otherwise the controller name with an indicator that it
   196  // is the name of a controller and not a model.
   197  func (c *switchCommand) name(controllerName string, machineReadable bool) (string, error) {
   198  	if controllerName == "" {
   199  		return "", nil
   200  	}
   201  	accountName, err := c.Store.CurrentAccount(controllerName)
   202  	if err == nil {
   203  		modelName, err := c.Store.CurrentModel(controllerName, accountName)
   204  		if err == nil {
   205  			return modelcmd.JoinModelName(controllerName, modelName), nil
   206  		} else if !errors.IsNotFound(err) {
   207  			return "", errors.Trace(err)
   208  		}
   209  	} else if !errors.IsNotFound(err) {
   210  		return "", errors.Trace(err)
   211  	}
   212  	// No current account or model.
   213  	if machineReadable {
   214  		return controllerName, nil
   215  	}
   216  	return fmt.Sprintf("%s (controller)", controllerName), nil
   217  }