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 }