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