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