github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/upgradecharm.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "fmt" 8 "os" 9 10 "github.com/juju/cmd" 11 "github.com/juju/names" 12 "gopkg.in/juju/charm.v4" 13 "launchpad.net/gnuflag" 14 15 "github.com/juju/juju/cmd/envcmd" 16 "github.com/juju/juju/cmd/juju/block" 17 "github.com/juju/juju/environs/config" 18 ) 19 20 // UpgradeCharm is responsible for upgrading a service's charm. 21 type UpgradeCharmCommand struct { 22 envcmd.EnvCommandBase 23 ServiceName string 24 Force bool 25 RepoPath string // defaults to JUJU_REPOSITORY 26 SwitchURL string 27 Revision int // defaults to -1 (latest) 28 } 29 30 const upgradeCharmDoc = ` 31 When no flags are set, the service's charm will be upgraded to the latest 32 revision available in the repository from which it was originally deployed. An 33 explicit revision can be chosen with the --revision flag. 34 35 If the charm came from a local repository, its path will be assumed to be 36 $JUJU_REPOSITORY unless overridden by --repository. 37 38 The local repository behaviour is tuned specifically to the workflow of a charm 39 author working on a single client machine; use of local repositories from 40 multiple clients is not supported and may lead to confusing behaviour. Each 41 local charm gets uploaded with the revision specified in the charm, if possible, 42 otherwise it gets a unique revision (highest in state + 1). 43 44 The --switch flag allows you to replace the charm with an entirely different 45 one. The new charm's URL and revision are inferred as they would be when running 46 a deploy command. 47 48 Please note that --switch is dangerous, because juju only has limited 49 information with which to determine compatibility; the operation will succeed, 50 regardless of potential havoc, so long as the following conditions hold: 51 52 - The new charm must declare all relations that the service is currently 53 participating in. 54 - All config settings shared by the old and new charms must 55 have the same types. 56 57 The new charm may add new relations and configuration settings. 58 59 --switch and --revision are mutually exclusive. To specify a given revision 60 number with --switch, give it in the charm URL, for instance "cs:wordpress-5" 61 would specify revision number 5 of the wordpress charm. 62 63 Use of the --force flag is not generally recommended; units upgraded while in an 64 error state will not have upgrade-charm hooks executed, and may cause unexpected 65 behavior. 66 ` 67 68 func (c *UpgradeCharmCommand) Info() *cmd.Info { 69 return &cmd.Info{ 70 Name: "upgrade-charm", 71 Args: "<service>", 72 Purpose: "upgrade a service's charm", 73 Doc: upgradeCharmDoc, 74 } 75 } 76 77 func (c *UpgradeCharmCommand) SetFlags(f *gnuflag.FlagSet) { 78 f.BoolVar(&c.Force, "force", false, "upgrade all units immediately, even if in error state") 79 f.StringVar(&c.RepoPath, "repository", os.Getenv("JUJU_REPOSITORY"), "local charm repository path") 80 f.StringVar(&c.SwitchURL, "switch", "", "crossgrade to a different charm") 81 f.IntVar(&c.Revision, "revision", -1, "explicit revision of current charm") 82 } 83 84 func (c *UpgradeCharmCommand) Init(args []string) error { 85 switch len(args) { 86 case 1: 87 if !names.IsValidService(args[0]) { 88 return fmt.Errorf("invalid service name %q", args[0]) 89 } 90 c.ServiceName = args[0] 91 case 0: 92 return fmt.Errorf("no service specified") 93 default: 94 return cmd.CheckEmpty(args[1:]) 95 } 96 if c.SwitchURL != "" && c.Revision != -1 { 97 return fmt.Errorf("--switch and --revision are mutually exclusive") 98 } 99 return nil 100 } 101 102 // Run connects to the specified environment and starts the charm 103 // upgrade process. 104 func (c *UpgradeCharmCommand) Run(ctx *cmd.Context) error { 105 client, err := c.NewAPIClient() 106 if err != nil { 107 return err 108 } 109 defer client.Close() 110 oldURL, err := client.ServiceGetCharmURL(c.ServiceName) 111 if err != nil { 112 return err 113 } 114 115 attrs, err := client.EnvironmentGet() 116 if err != nil { 117 return err 118 } 119 conf, err := config.New(config.NoDefaults, attrs) 120 if err != nil { 121 return err 122 } 123 124 var newURL *charm.URL 125 if c.SwitchURL != "" { 126 newURL, err = resolveCharmURL(c.SwitchURL, client, conf) 127 if err != nil { 128 return err 129 } 130 } else { 131 // No new URL specified, but revision might have been. 132 newURL = oldURL.WithRevision(c.Revision) 133 } 134 135 repo, err := charm.InferRepository(newURL.Reference(), ctx.AbsPath(c.RepoPath)) 136 if err != nil { 137 return err 138 } 139 config.SpecializeCharmRepo(repo, conf) 140 141 // If no explicit revision was set with either SwitchURL 142 // or Revision flags, discover the latest. 143 explicitRevision := true 144 if newURL.Revision == -1 { 145 explicitRevision = false 146 latest, err := charm.Latest(repo, newURL) 147 if err != nil { 148 return err 149 } 150 newURL = newURL.WithRevision(latest) 151 } 152 if *newURL == *oldURL { 153 if explicitRevision { 154 return fmt.Errorf("already running specified charm %q", newURL) 155 } else if newURL.Schema == "cs" { 156 // No point in trying to upgrade a charm store charm when 157 // we just determined that's the latest revision 158 // available. 159 return fmt.Errorf("already running latest charm %q", newURL) 160 } 161 } 162 163 addedURL, err := addCharmViaAPI(client, ctx, newURL, repo) 164 if err != nil { 165 return block.ProcessBlockedError(err, block.BlockChange) 166 } 167 168 return block.ProcessBlockedError(client.ServiceSetCharm(c.ServiceName, addedURL.String(), c.Force), block.BlockChange) 169 }