github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/cmd/juju/set.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "errors" 8 "fmt" 9 "strings" 10 11 "launchpad.net/gnuflag" 12 13 "launchpad.net/juju-core/cmd" 14 "launchpad.net/juju-core/juju" 15 "launchpad.net/juju-core/state/api/params" 16 ) 17 18 // SetCommand updates the configuration of a service. 19 type SetCommand struct { 20 cmd.EnvCommandBase 21 ServiceName string 22 SettingsStrings map[string]string 23 SettingsYAML cmd.FileVar 24 } 25 26 const setDoc = ` 27 Set one or more configuration options for the specified service. See also the 28 unset command which sets one or more configuration options for a specified 29 service to their default value. 30 ` 31 32 func (c *SetCommand) Info() *cmd.Info { 33 return &cmd.Info{ 34 Name: "set", 35 Args: "<service> name=value ...", 36 Purpose: "set service config options", 37 Doc: "Set one or more configuration options for the specified service.", 38 } 39 } 40 41 func (c *SetCommand) SetFlags(f *gnuflag.FlagSet) { 42 c.EnvCommandBase.SetFlags(f) 43 f.Var(&c.SettingsYAML, "config", "path to yaml-formatted service config") 44 } 45 46 func (c *SetCommand) Init(args []string) error { 47 if len(args) == 0 || len(strings.Split(args[0], "=")) > 1 { 48 return errors.New("no service name specified") 49 } 50 if c.SettingsYAML.Path != "" && len(args) > 1 { 51 return errors.New("cannot specify --config when using key=value arguments") 52 } 53 c.ServiceName = args[0] 54 settings, err := parse(args[1:]) 55 if err != nil { 56 return err 57 } 58 c.SettingsStrings = settings 59 return nil 60 } 61 62 // serviceSet1dot16 does the final ServiceSet step using direct DB access 63 // compatibility with an API server running 1.16 or older (when ServiceUnset 64 // was not available). This fallback can be removed when we no longer maintain 65 // 1.16 compatibility. 66 // This was copied directly from the code in SetCommand.Run in 1.16 67 func (c *SetCommand) serviceSet1dot16() error { 68 conn, err := juju.NewConnFromName(c.EnvName) 69 if err != nil { 70 return err 71 } 72 defer conn.Close() 73 service, err := conn.State.Service(c.ServiceName) 74 if err != nil { 75 return err 76 } 77 ch, _, err := service.Charm() 78 if err != nil { 79 return err 80 } 81 // We don't need the multiple logic here, because that should have 82 // already been taken care of by the API code (which *was* in 1.16). 83 settings, err := ch.Config().ParseSettingsStrings(c.SettingsStrings) 84 if err != nil { 85 return err 86 } 87 return service.UpdateConfigSettings(settings) 88 } 89 90 // Run updates the configuration of a service. 91 func (c *SetCommand) Run(ctx *cmd.Context) error { 92 api, err := juju.NewAPIClientFromName(c.EnvName) 93 if err != nil { 94 return err 95 } 96 defer api.Close() 97 98 if c.SettingsYAML.Path != "" { 99 b, err := c.SettingsYAML.Read(ctx) 100 if err != nil { 101 return err 102 } 103 return api.ServiceSetYAML(c.ServiceName, string(b)) 104 } else if len(c.SettingsStrings) == 0 { 105 return nil 106 } 107 err = api.ServiceSet(c.ServiceName, c.SettingsStrings) 108 if params.IsCodeNotImplemented(err) { 109 logger.Infof("NewServiceSetForClientAPI not supported by the API server, " + 110 "falling back to 1.16 compatibility mode (direct DB access)") 111 err = c.serviceSet1dot16() 112 } 113 return err 114 } 115 116 // parse parses the option k=v strings into a map of options to be 117 // updated in the config. Keys with empty values are returned separately 118 // and should be removed. 119 func parse(options []string) (map[string]string, error) { 120 kv := make(map[string]string) 121 for _, o := range options { 122 s := strings.SplitN(o, "=", 2) 123 if len(s) != 2 || s[0] == "" { 124 return nil, fmt.Errorf("invalid option: %q", o) 125 } 126 kv[s[0]] = s[1] 127 } 128 return kv, nil 129 }