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 }