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 }