github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/uniter/runner/jujuc/relation-set.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package jujuc 5 6 import ( 7 "fmt" 8 "io" 9 "io/ioutil" 10 11 "github.com/juju/cmd" 12 "github.com/juju/errors" 13 "github.com/juju/utils/keyvalues" 14 goyaml "gopkg.in/yaml.v1" 15 "launchpad.net/gnuflag" 16 ) 17 18 const relationSetDoc = ` 19 "relation-set" writes the local unit's settings for some relation. 20 If no relation is specified then the current relation is used. The 21 setting values are not inspected and are stored as strings. Setting 22 an empty string causes the setting to be removed. Duplicate settings 23 are not allowed. 24 25 The --file option should be used when one or more key-value pairs are 26 too long to fit within the command length limit of the shell or 27 operating system. The file will contain a YAML map containing the 28 settings. Settings in the file will be overridden by any duplicate 29 key-value arguments. A value of "-" for the filename means <stdin>. 30 ` 31 32 // RelationSetCommand implements the relation-set command. 33 type RelationSetCommand struct { 34 cmd.CommandBase 35 ctx Context 36 RelationId int 37 relationIdProxy gnuflag.Value 38 Settings map[string]string 39 settingsFile cmd.FileVar 40 formatFlag string // deprecated 41 } 42 43 func NewRelationSetCommand(ctx Context) (cmd.Command, error) { 44 c := &RelationSetCommand{ctx: ctx} 45 46 rV, err := newRelationIdValue(ctx, &c.RelationId) 47 if err != nil { 48 return nil, errors.Trace(err) 49 } 50 c.relationIdProxy = rV 51 52 return c, nil 53 } 54 55 func (c *RelationSetCommand) Info() *cmd.Info { 56 return &cmd.Info{ 57 Name: "relation-set", 58 Args: "key=value [key=value ...]", 59 Purpose: "set relation settings", 60 Doc: relationSetDoc, 61 } 62 } 63 64 func (c *RelationSetCommand) SetFlags(f *gnuflag.FlagSet) { 65 f.Var(c.relationIdProxy, "r", "specify a relation by id") 66 f.Var(c.relationIdProxy, "relation", "") 67 68 c.settingsFile.SetStdin() 69 f.Var(&c.settingsFile, "file", "file containing key-value pairs") 70 71 f.StringVar(&c.formatFlag, "format", "", "deprecated format flag") 72 } 73 74 func (c *RelationSetCommand) Init(args []string) error { 75 if c.RelationId == -1 { 76 return errors.Errorf("no relation id specified") 77 } 78 79 // The overrides will be applied during Run when c.settingsFile is handled. 80 overrides, err := keyvalues.Parse(args, true) 81 if err != nil { 82 return errors.Trace(err) 83 } 84 c.Settings = overrides 85 return nil 86 } 87 88 func (c *RelationSetCommand) readSettings(in io.Reader) (map[string]string, error) { 89 data, err := ioutil.ReadAll(in) 90 if err != nil { 91 return nil, errors.Trace(err) 92 } 93 94 skipValidation := false // for debugging 95 if !skipValidation { 96 // Can this validation be done more simply or efficiently? 97 98 var scalar string 99 if err := goyaml.Unmarshal(data, &scalar); err != nil { 100 return nil, errors.Trace(err) 101 } 102 if scalar != "" { 103 return nil, errors.Errorf("expected YAML map, got %q", scalar) 104 } 105 106 var sequence []string 107 if err := goyaml.Unmarshal(data, &sequence); err != nil { 108 return nil, errors.Trace(err) 109 } 110 if len(sequence) != 0 { 111 return nil, errors.Errorf("expected YAML map, got %#v", sequence) 112 } 113 } 114 115 kvs := make(map[string]string) 116 if err := goyaml.Unmarshal(data, kvs); err != nil { 117 return nil, errors.Trace(err) 118 } 119 120 return kvs, nil 121 } 122 123 func (c *RelationSetCommand) handleSettingsFile(ctx *cmd.Context) error { 124 if c.settingsFile.Path == "" { 125 return nil 126 } 127 128 file, err := c.settingsFile.Open(ctx) 129 if err != nil { 130 return errors.Trace(err) 131 } 132 defer file.Close() 133 134 settings, err := c.readSettings(file) 135 if err != nil { 136 return errors.Trace(err) 137 } 138 139 overrides := c.Settings 140 for k, v := range overrides { 141 settings[k] = v 142 } 143 c.Settings = settings 144 return nil 145 } 146 147 func (c *RelationSetCommand) Run(ctx *cmd.Context) (err error) { 148 if c.formatFlag != "" { 149 fmt.Fprintf(ctx.Stderr, "--format flag deprecated for command %q", c.Info().Name) 150 } 151 if err := c.handleSettingsFile(ctx); err != nil { 152 return errors.Trace(err) 153 } 154 155 r, err := c.ctx.Relation(c.RelationId) 156 if err != nil { 157 return errors.Trace(err) 158 } 159 settings, err := r.Settings() 160 if err != nil { 161 return errors.Annotate(err, "cannot read relation settings") 162 } 163 for k, v := range c.Settings { 164 if v != "" { 165 settings.Set(k, v) 166 } else { 167 settings.Delete(k) 168 } 169 } 170 return nil 171 }