github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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 "io/ioutil" 10 "os" 11 "strings" 12 "unicode/utf8" 13 14 "github.com/juju/cmd" 15 "github.com/juju/utils/keyvalues" 16 "launchpad.net/gnuflag" 17 18 "github.com/juju/juju/cmd/envcmd" 19 "github.com/juju/juju/cmd/juju/block" 20 ) 21 22 // SetCommand updates the configuration of a service. 23 type SetCommand struct { 24 envcmd.EnvCommandBase 25 ServiceName string 26 SettingsStrings map[string]string 27 SettingsYAML cmd.FileVar 28 } 29 30 const setDoc = ` 31 Set one or more configuration options for the specified service. See also the 32 unset command which sets one or more configuration options for a specified 33 service to their default value. 34 35 In case a value starts with an at sign (@) the rest of the value is interpreted 36 as a filename. The value itself is then read out of the named file. The maximum 37 size of this value is 5M. 38 39 Option values may be any UTF-8 encoded string. UTF-8 is accepted on the command 40 line and in configuration files. 41 ` 42 43 const maxValueSize = 5242880 44 45 func (c *SetCommand) Info() *cmd.Info { 46 return &cmd.Info{ 47 Name: "set", 48 Args: "<service> name=value ...", 49 Purpose: "set service config options", 50 Doc: setDoc, 51 } 52 } 53 54 func (c *SetCommand) SetFlags(f *gnuflag.FlagSet) { 55 f.Var(&c.SettingsYAML, "config", "path to yaml-formatted service config") 56 } 57 58 func (c *SetCommand) Init(args []string) error { 59 if len(args) == 0 || len(strings.Split(args[0], "=")) > 1 { 60 return errors.New("no service name specified") 61 } 62 if c.SettingsYAML.Path != "" && len(args) > 1 { 63 return errors.New("cannot specify --config when using key=value arguments") 64 } 65 c.ServiceName = args[0] 66 settings, err := keyvalues.Parse(args[1:], true) 67 if err != nil { 68 return err 69 } 70 c.SettingsStrings = settings 71 return nil 72 } 73 74 // Run updates the configuration of a service. 75 func (c *SetCommand) Run(ctx *cmd.Context) error { 76 api, err := c.NewAPIClient() 77 if err != nil { 78 return err 79 } 80 defer api.Close() 81 82 if c.SettingsYAML.Path != "" { 83 b, err := c.SettingsYAML.Read(ctx) 84 if err != nil { 85 return err 86 } 87 return block.ProcessBlockedError(api.ServiceSetYAML(c.ServiceName, string(b)), block.BlockChange) 88 } else if len(c.SettingsStrings) == 0 { 89 return nil 90 } 91 settings := map[string]string{} 92 for k, v := range c.SettingsStrings { 93 //empty string is also valid as a setting value 94 if v == "" { 95 settings[k] = v 96 continue 97 } 98 99 if v[0] != '@' { 100 if !utf8.ValidString(v) { 101 return fmt.Errorf("value for option %q contains non-UTF-8 sequences", k) 102 } 103 settings[k] = v 104 continue 105 } 106 nv, err := readValue(ctx, v[1:]) 107 if err != nil { 108 return err 109 } 110 if !utf8.ValidString(nv) { 111 return fmt.Errorf("value for option %q contains non-UTF-8 sequences", k) 112 } 113 settings[k] = nv 114 } 115 116 result, err := api.ServiceGet(c.ServiceName) 117 if err != nil { 118 return err 119 } 120 121 for k, v := range settings { 122 configValue := result.Config[k] 123 124 configValueMap, ok := configValue.(map[string]interface{}) 125 if ok { 126 // convert the value to string and compare 127 if fmt.Sprintf("%v", configValueMap["value"]) == v { 128 logger.Warningf("the configuration setting %q already has the value %q", k, v) 129 } 130 } 131 } 132 133 return block.ProcessBlockedError(api.ServiceSet(c.ServiceName, settings), block.BlockChange) 134 } 135 136 // readValue reads the value of an option out of the named file. 137 // An empty content is valid, like in parsing the options. The upper 138 // size is 5M. 139 func readValue(ctx *cmd.Context, filename string) (string, error) { 140 absFilename := ctx.AbsPath(filename) 141 fi, err := os.Stat(absFilename) 142 if err != nil { 143 return "", fmt.Errorf("cannot read option from file %q: %v", filename, err) 144 } 145 if fi.Size() > maxValueSize { 146 return "", fmt.Errorf("size of option file is larger than 5M") 147 } 148 content, err := ioutil.ReadFile(ctx.AbsPath(filename)) 149 if err != nil { 150 return "", fmt.Errorf("cannot read option from file %q: %v", filename, err) 151 } 152 return string(content), nil 153 }