github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/commands/main.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package commands 5 6 import ( 7 "bytes" 8 "fmt" 9 "os" 10 "os/exec" 11 "strings" 12 "text/template" 13 14 "github.com/juju/cmd" 15 "github.com/juju/errors" 16 "github.com/juju/loggo" 17 utilsos "github.com/juju/os" 18 "github.com/juju/os/series" 19 proxyutils "github.com/juju/proxy" 20 "github.com/juju/utils/featureflag" 21 "github.com/juju/version" 22 23 // Import the providers. 24 cloudfile "github.com/juju/juju/cloud" 25 jujucmd "github.com/juju/juju/cmd" 26 "github.com/juju/juju/cmd/juju/action" 27 "github.com/juju/juju/cmd/juju/application" 28 "github.com/juju/juju/cmd/juju/backups" 29 "github.com/juju/juju/cmd/juju/block" 30 "github.com/juju/juju/cmd/juju/caas" 31 "github.com/juju/juju/cmd/juju/cachedimages" 32 "github.com/juju/juju/cmd/juju/charmcmd" 33 "github.com/juju/juju/cmd/juju/cloud" 34 "github.com/juju/juju/cmd/juju/controller" 35 "github.com/juju/juju/cmd/juju/crossmodel" 36 "github.com/juju/juju/cmd/juju/firewall" 37 "github.com/juju/juju/cmd/juju/gui" 38 "github.com/juju/juju/cmd/juju/machine" 39 "github.com/juju/juju/cmd/juju/metricsdebug" 40 "github.com/juju/juju/cmd/juju/model" 41 "github.com/juju/juju/cmd/juju/resource" 42 rcmd "github.com/juju/juju/cmd/juju/romulus/commands" 43 "github.com/juju/juju/cmd/juju/setmeterstatus" 44 "github.com/juju/juju/cmd/juju/space" 45 "github.com/juju/juju/cmd/juju/status" 46 "github.com/juju/juju/cmd/juju/storage" 47 "github.com/juju/juju/cmd/juju/subnet" 48 "github.com/juju/juju/cmd/juju/user" 49 "github.com/juju/juju/cmd/modelcmd" 50 "github.com/juju/juju/feature" 51 "github.com/juju/juju/juju" 52 "github.com/juju/juju/juju/osenv" 53 "github.com/juju/juju/jujuclient" 54 _ "github.com/juju/juju/provider/all" 55 "github.com/juju/juju/resource/resourceadapters" 56 "github.com/juju/juju/utils/proxy" 57 jujuversion "github.com/juju/juju/version" 58 ) 59 60 var logger = loggo.GetLogger("juju.cmd.juju.commands") 61 62 func init() { 63 featureflag.SetFlagsFromEnvironment(osenv.JujuFeatureFlagEnvKey) 64 } 65 66 // TODO(ericsnow) Move the following to cmd/juju/main.go: 67 // jujuDoc 68 // Main 69 70 var jujuDoc = ` 71 juju provides easy, intelligent application orchestration on top of cloud 72 infrastructure providers such as Amazon EC2, MaaS, OpenStack, Windows, Azure, 73 or your local machine. 74 75 https://jujucharms.com/ 76 ` 77 78 const juju1xCmdName = "juju-1" 79 80 var usageHelp = ` 81 Usage: juju [help] <command> 82 83 Summary: 84 Juju is model & application management software designed to leverage the power 85 of existing resource pools, particularly cloud-based ones. It has built-in 86 support for cloud providers such as Amazon EC2, Google GCE, Microsoft 87 Azure, OpenStack, and Rackspace. It also works very well with MAAS and 88 LXD. Juju allows for easy installation and management of workloads on a 89 chosen resource pool. 90 91 See https://jujucharms.com/docs/stable for documentation. 92 93 Common commands: 94 95 add-cloud Adds a user-defined cloud to Juju. 96 add-credential Adds or replaces credentials for a cloud. 97 add-model Adds a hosted model. 98 add-relation Adds a relation between two applications. 99 add-unit Adds extra units of a deployed application. 100 add-user Adds a Juju user to a controller. 101 bootstrap Initializes a cloud environment. 102 controllers Lists all controllers. 103 deploy Deploys a new application. 104 expose Makes an application publicly available over the network. 105 models Lists models a user can access on a controller. 106 status Displays the current status of Juju, applications, and units. 107 switch Selects or identifies the current controller and model. 108 109 Example help commands: 110 111 `[1:] + "`juju help`" + ` This help page 112 ` + "`juju help commands`" + ` Lists all commands 113 ` + "`juju help deploy`" + ` Shows help for command 'deploy' 114 ` 115 116 var x = []byte("\x96\x8c\x8a\x91\x93\x9a\x9e\x8c\x97\x99\x8a\x9c\x94\x96\x91\x98\xdf\x9e\x92\x9e\x85\x96\x91\x98\xf5") 117 118 // Main registers subcommands for the juju executable, and hands over control 119 // to the cmd package. This function is not redundant with main, because it 120 // provides an entry point for testing with arbitrary command line arguments. 121 // This function returns the exit code, for main to pass to os.Exit. 122 func Main(args []string) int { 123 return main{ 124 execCommand: exec.Command, 125 }.Run(args) 126 } 127 128 // main is a type that captures dependencies for running the main function. 129 type main struct { 130 // execCommand abstracts away exec.Command. 131 execCommand func(command string, args ...string) *exec.Cmd 132 } 133 134 // Run is the main entry point for the juju client. 135 func (m main) Run(args []string) int { 136 ctx, err := cmd.DefaultContext() 137 if err != nil { 138 cmd.WriteError(os.Stderr, err) 139 return 2 140 } 141 142 // note that this has to come before we init the juju home directory, 143 // since it relies on detecting the lack of said directory. 144 newInstall := m.maybeWarnJuju1x() 145 146 if err = juju.InitJujuXDGDataHome(); err != nil { 147 cmd.WriteError(ctx.Stderr, err) 148 return 2 149 } 150 151 if err := installProxy(); err != nil { 152 cmd.WriteError(ctx.Stderr, err) 153 return 2 154 } 155 156 if newInstall { 157 fmt.Fprintf(ctx.Stderr, "Since Juju %v is being run for the first time, downloading latest cloud information.\n", jujuversion.Current.Major) 158 updateCmd := cloud.NewUpdateCloudsCommand() 159 if err := updateCmd.Run(ctx); err != nil { 160 cmd.WriteError(ctx.Stderr, err) 161 } 162 } 163 164 for i := range x { 165 x[i] ^= 255 166 } 167 if len(args) == 2 { 168 if args[1] == string(x[0:2]) { 169 os.Stdout.Write(x[9:]) 170 return 0 171 } 172 if args[1] == string(x[2:9]) { 173 os.Stdout.Write(model.ExtractCert()) 174 return 0 175 } 176 } 177 178 jcmd := NewJujuCommand(ctx) 179 return cmd.Main(jcmd, ctx, args[1:]) 180 } 181 182 func installProxy() error { 183 // Set the default transport to use the in-process proxy 184 // configuration. 185 if err := proxy.DefaultConfig.Set(proxyutils.DetectProxies()); err != nil { 186 return errors.Trace(err) 187 } 188 if err := proxy.DefaultConfig.InstallInDefaultTransport(); err != nil { 189 return errors.Trace(err) 190 } 191 return nil 192 } 193 194 func (m main) maybeWarnJuju1x() (newInstall bool) { 195 newInstall = !juju2xConfigDataExists() 196 if !shouldWarnJuju1x() { 197 return newInstall 198 } 199 ver, exists := m.juju1xVersion() 200 if !exists { 201 return newInstall 202 } 203 // TODO (anastasiamac 2016-10-21) Once manual page exists as per 204 // https://github.com/juju/docs/issues/1487, 205 // link it in the Note below to avoid propose here. 206 welcomeMsgTemplate := ` 207 Welcome to Juju {{.CurrentJujuVersion}}. 208 See https://jujucharms.com/docs/stable/introducing-2 for more details. 209 210 If you want to use Juju {{.OldJujuVersion}}, run 'juju' commands as '{{.OldJujuCommand}}'. For example, '{{.OldJujuCommand}} bootstrap'. 211 See https://jujucharms.com/docs/stable/juju-coexist for installation details. 212 `[1:] 213 t := template.Must(template.New("plugin").Parse(welcomeMsgTemplate)) 214 var buf bytes.Buffer 215 t.Execute(&buf, map[string]interface{}{ 216 "CurrentJujuVersion": jujuversion.Current, 217 "OldJujuVersion": ver, 218 "OldJujuCommand": juju1xCmdName, 219 }) 220 fmt.Fprintln(os.Stderr, buf.String()) 221 return newInstall 222 } 223 224 func (m main) juju1xVersion() (ver string, exists bool) { 225 out, err := m.execCommand(juju1xCmdName, "version").Output() 226 if err == exec.ErrNotFound { 227 return "", false 228 } 229 ver = "1.x" 230 if err == nil { 231 v := strings.TrimSpace(string(out)) 232 // parse so we can drop the series and arch 233 bin, err := version.ParseBinary(v) 234 if err == nil { 235 ver = bin.Number.String() 236 } 237 } 238 return ver, true 239 } 240 241 func shouldWarnJuju1x() bool { 242 // this code only applies to Ubuntu, where we renamed Juju 1.x to juju-1. 243 ostype, err := series.GetOSFromSeries(series.MustHostSeries()) 244 if err != nil || ostype != utilsos.Ubuntu { 245 return false 246 } 247 return osenv.Juju1xEnvConfigExists() && !juju2xConfigDataExists() 248 } 249 250 func juju2xConfigDataExists() bool { 251 _, err := os.Stat(osenv.JujuXDGDataHomeDir()) 252 return err == nil 253 } 254 255 // NewJujuCommand ... 256 func NewJujuCommand(ctx *cmd.Context) cmd.Command { 257 jcmd := jujucmd.NewSuperCommand(cmd.SuperCommandParams{ 258 Name: "juju", 259 Doc: jujuDoc, 260 MissingCallback: RunPlugin, 261 UserAliasesFilename: osenv.JujuXDGDataHomePath("aliases"), 262 FlagKnownAs: "option", 263 }) 264 jcmd.AddHelpTopic("basics", "Basic Help Summary", usageHelp) 265 registerCommands(jcmd, ctx) 266 return jcmd 267 } 268 269 type commandRegistry interface { 270 Register(cmd.Command) 271 RegisterSuperAlias(name, super, forName string, check cmd.DeprecationCheck) 272 RegisterDeprecated(subcmd cmd.Command, check cmd.DeprecationCheck) 273 } 274 275 // TODO(ericsnow) Factor out the commands and aliases into a static 276 // registry that can be passed to the supercommand separately. 277 278 // registerCommands registers commands in the specified registry. 279 func registerCommands(r commandRegistry, ctx *cmd.Context) { 280 // Creation commands. 281 r.Register(newBootstrapCommand()) 282 r.Register(application.NewAddRelationCommand()) 283 284 // Cross model relations commands. 285 r.Register(crossmodel.NewOfferCommand()) 286 r.Register(crossmodel.NewRemoveOfferCommand()) 287 r.Register(crossmodel.NewShowOfferedEndpointCommand()) 288 r.Register(crossmodel.NewListEndpointsCommand()) 289 r.Register(crossmodel.NewFindEndpointsCommand()) 290 r.Register(application.NewConsumeCommand()) 291 r.Register(application.NewSuspendRelationCommand()) 292 r.Register(application.NewResumeRelationCommand()) 293 294 // Firewall rule commands. 295 r.Register(firewall.NewSetFirewallRuleCommand()) 296 r.Register(firewall.NewListFirewallRulesCommand()) 297 298 // Destruction commands. 299 r.Register(application.NewRemoveRelationCommand()) 300 r.Register(application.NewRemoveApplicationCommand()) 301 r.Register(application.NewRemoveUnitCommand()) 302 r.Register(application.NewRemoveSaasCommand()) 303 304 // Reporting commands. 305 r.Register(status.NewStatusCommand()) 306 r.Register(newSwitchCommand()) 307 r.Register(status.NewStatusHistoryCommand()) 308 309 // Error resolution and debugging commands. 310 r.Register(newDefaultRunCommand(nil)) 311 r.Register(newSCPCommand(nil)) 312 r.Register(newSSHCommand(nil, nil)) 313 r.Register(application.NewResolvedCommand()) 314 r.Register(newDebugLogCommand(nil)) 315 r.Register(newDebugHooksCommand(nil)) 316 317 // Configuration commands. 318 r.Register(model.NewModelGetConstraintsCommand()) 319 r.Register(model.NewModelSetConstraintsCommand()) 320 r.Register(newSyncToolsCommand()) 321 r.Register(newUpgradeJujuCommand(nil, nil)) 322 r.Register(application.NewUpgradeCharmCommand()) 323 r.Register(application.NewSetSeriesCommand()) 324 325 // Charm tool commands. 326 r.Register(newHelpToolCommand()) 327 // TODO (anastasiamac 2017-08-1) This needs to be removed in Juju 3.x 328 // lp#1707836 329 r.Register(charmcmd.NewSuperCommand()) 330 331 // Manage backups. 332 r.Register(backups.NewCreateCommand()) 333 r.Register(backups.NewDownloadCommand()) 334 r.Register(backups.NewShowCommand()) 335 r.Register(backups.NewListCommand()) 336 r.Register(backups.NewRemoveCommand()) 337 r.Register(backups.NewRestoreCommand()) 338 r.Register(backups.NewUploadCommand()) 339 340 // Manage authorized ssh keys. 341 r.Register(NewAddKeysCommand()) 342 r.Register(NewRemoveKeysCommand()) 343 r.Register(NewImportKeysCommand()) 344 r.Register(NewListKeysCommand()) 345 346 // Manage users and access 347 r.Register(user.NewAddCommand()) 348 r.Register(user.NewChangePasswordCommand()) 349 r.Register(user.NewShowUserCommand()) 350 r.Register(user.NewListCommand()) 351 r.Register(user.NewEnableCommand()) 352 r.Register(user.NewDisableCommand()) 353 r.Register(user.NewLoginCommand()) 354 r.Register(user.NewLogoutCommand()) 355 r.Register(user.NewRemoveCommand()) 356 r.Register(user.NewWhoAmICommand()) 357 358 // Manage cached images 359 r.Register(cachedimages.NewRemoveCommand()) 360 r.Register(cachedimages.NewListCommand()) 361 362 // Manage machines 363 r.Register(machine.NewAddCommand()) 364 r.Register(machine.NewRemoveCommand()) 365 r.Register(machine.NewListMachinesCommand()) 366 r.Register(machine.NewShowMachineCommand()) 367 r.Register(machine.NewUpgradeSeriesCommand()) 368 369 // Manage model 370 r.Register(model.NewConfigCommand()) 371 r.Register(model.NewDefaultsCommand()) 372 r.Register(model.NewRetryProvisioningCommand()) 373 r.Register(model.NewDestroyCommand()) 374 r.Register(model.NewGrantCommand()) 375 r.Register(model.NewRevokeCommand()) 376 r.Register(model.NewShowCommand()) 377 r.Register(model.NewModelCredentialCommand()) 378 if featureflag.Enabled(feature.Generations) { 379 r.Register(model.NewAddGenerationCommand()) 380 r.Register(model.NewCancelGenerationCommand()) 381 r.Register(model.NewAdvanceGenerationCommand()) 382 r.Register(model.NewSwitchGenerationCommand()) 383 } 384 385 r.Register(newMigrateCommand()) 386 r.Register(model.NewExportBundleCommand()) 387 388 if featureflag.Enabled(feature.DeveloperMode) { 389 r.Register(model.NewDumpCommand()) 390 r.Register(model.NewDumpDBCommand()) 391 } 392 393 // Manage and control actions 394 r.Register(action.NewStatusCommand()) 395 r.Register(action.NewRunCommand()) 396 r.Register(action.NewShowOutputCommand()) 397 r.Register(action.NewListCommand()) 398 r.Register(action.NewCancelCommand()) 399 400 // Manage controller availability 401 r.Register(newEnableHACommand()) 402 403 // Manage and control applications 404 r.Register(application.NewAddUnitCommand()) 405 r.Register(application.NewConfigCommand()) 406 r.Register(application.NewDeployCommand()) 407 r.Register(application.NewExposeCommand()) 408 r.Register(application.NewUnexposeCommand()) 409 r.Register(application.NewApplicationGetConstraintsCommand()) 410 r.Register(application.NewApplicationSetConstraintsCommand()) 411 r.Register(application.NewBundleDiffCommand()) 412 r.Register(application.NewShowApplicationCommand()) 413 414 // Operation protection commands 415 r.Register(block.NewDisableCommand()) 416 r.Register(block.NewListCommand()) 417 r.Register(block.NewEnableCommand()) 418 419 // Manage storage 420 r.Register(storage.NewAddCommand()) 421 r.Register(storage.NewListCommand()) 422 r.Register(storage.NewPoolCreateCommand()) 423 r.Register(storage.NewPoolListCommand()) 424 r.Register(storage.NewPoolRemoveCommand()) 425 r.Register(storage.NewPoolUpdateCommand()) 426 r.Register(storage.NewShowCommand()) 427 r.Register(storage.NewRemoveStorageCommandWithAPI()) 428 r.Register(storage.NewDetachStorageCommandWithAPI()) 429 r.Register(storage.NewAttachStorageCommandWithAPI()) 430 r.Register(storage.NewImportFilesystemCommand(storage.NewStorageImporter, nil)) 431 432 // Manage spaces 433 r.Register(space.NewAddCommand()) 434 r.Register(space.NewListCommand()) 435 r.Register(space.NewReloadCommand()) 436 if featureflag.Enabled(feature.PostNetCLIMVP) { 437 r.Register(space.NewRemoveCommand()) 438 r.Register(space.NewUpdateCommand()) 439 r.Register(space.NewRenameCommand()) 440 } 441 442 // Manage subnets 443 r.Register(subnet.NewAddCommand()) 444 r.Register(subnet.NewListCommand()) 445 if featureflag.Enabled(feature.PostNetCLIMVP) { 446 r.Register(subnet.NewCreateCommand()) 447 r.Register(subnet.NewRemoveCommand()) 448 } 449 450 // Manage controllers 451 r.Register(controller.NewAddModelCommand()) 452 r.Register(controller.NewDestroyCommand()) 453 r.Register(controller.NewListModelsCommand()) 454 r.Register(controller.NewKillCommand()) 455 r.Register(controller.NewListControllersCommand()) 456 r.Register(controller.NewRegisterCommand()) 457 r.Register(controller.NewUnregisterCommand(jujuclient.NewFileClientStore())) 458 r.Register(controller.NewEnableDestroyControllerCommand()) 459 r.Register(controller.NewShowControllerCommand()) 460 r.Register(controller.NewConfigCommand()) 461 462 // Debug Metrics 463 r.Register(metricsdebug.New()) 464 r.Register(metricsdebug.NewCollectMetricsCommand()) 465 r.Register(setmeterstatus.New()) 466 467 // Manage clouds and credentials 468 r.Register(cloud.NewUpdateCloudsCommand()) 469 r.Register(cloud.NewListCloudsCommand()) 470 r.Register(cloud.NewListRegionsCommand()) 471 r.Register(cloud.NewShowCloudCommand()) 472 r.Register(cloud.NewAddCloudCommand(&cloudToCommandAdapter{})) 473 r.Register(cloud.NewRemoveCloudCommand()) 474 r.Register(cloud.NewListCredentialsCommand()) 475 r.Register(cloud.NewDetectCredentialsCommand()) 476 r.Register(cloud.NewSetDefaultRegionCommand()) 477 r.Register(cloud.NewSetDefaultCredentialCommand()) 478 r.Register(cloud.NewAddCredentialCommand()) 479 r.Register(cloud.NewRemoveCredentialCommand()) 480 r.Register(cloud.NewUpdateCredentialCommand()) 481 r.Register(cloud.NewShowCredentialCommand()) 482 r.Register(model.NewGrantCloudCommand()) 483 r.Register(model.NewRevokeCloudCommand()) 484 485 // CAAS commands 486 r.Register(caas.NewAddCAASCommand(&cloudToCommandAdapter{})) 487 r.Register(caas.NewRemoveCAASCommand(&cloudToCommandAdapter{})) 488 r.Register(application.NewScaleApplicationCommand()) 489 490 // Manage Application Credential Access 491 r.Register(application.NewTrustCommand()) 492 493 // Juju GUI commands. 494 r.Register(gui.NewGUICommand()) 495 r.Register(gui.NewUpgradeGUICommand()) 496 497 // Resource commands 498 r.Register(resource.NewUploadCommand(resource.UploadDeps{ 499 NewClient: func(c *resource.UploadCommand) (resource.UploadClient, error) { 500 apiRoot, err := c.NewAPIRoot() 501 if err != nil { 502 return nil, errors.Trace(err) 503 } 504 return resourceadapters.NewAPIClient(apiRoot) 505 }, 506 OpenResource: func(s string) (resource.ReadSeekCloser, error) { 507 return os.Open(s) 508 }, 509 })) 510 r.Register(resource.NewListCommand(resource.ListDeps{ 511 NewClient: func(c *resource.ListCommand) (resource.ListClient, error) { 512 apiRoot, err := c.NewAPIRoot() 513 if err != nil { 514 return nil, errors.Trace(err) 515 } 516 return resourceadapters.NewAPIClient(apiRoot) 517 }, 518 })) 519 r.Register(resource.NewCharmResourcesCommand(nil)) 520 521 // Commands registered elsewhere. 522 for _, newCommand := range registeredCommands { 523 command := newCommand() 524 r.Register(command) 525 } 526 for _, newCommand := range registeredEnvCommands { 527 command := newCommand() 528 r.Register(modelcmd.Wrap(command)) 529 } 530 rcmd.RegisterAll(r) 531 } 532 533 type cloudToCommandAdapter struct{} 534 535 func (cloudToCommandAdapter) ParseCloudMetadataFile(path string) (map[string]cloudfile.Cloud, error) { 536 return cloudfile.ParseCloudMetadataFile(path) 537 } 538 func (cloudToCommandAdapter) ParseOneCloud(data []byte) (cloudfile.Cloud, error) { 539 return cloudfile.ParseOneCloud(data) 540 } 541 func (cloudToCommandAdapter) PublicCloudMetadata(searchPaths ...string) (map[string]cloudfile.Cloud, bool, error) { 542 return cloudfile.PublicCloudMetadata(searchPaths...) 543 } 544 func (cloudToCommandAdapter) PersonalCloudMetadata() (map[string]cloudfile.Cloud, error) { 545 return cloudfile.PersonalCloudMetadata() 546 } 547 func (cloudToCommandAdapter) WritePersonalCloudMetadata(cloudsMap map[string]cloudfile.Cloud) error { 548 return cloudfile.WritePersonalCloudMetadata(cloudsMap) 549 }