github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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 Settings map[string]string 38 settingsFile cmd.FileVar 39 formatFlag string // deprecated 40 } 41 42 func NewRelationSetCommand(ctx Context) cmd.Command { 43 return &RelationSetCommand{ctx: ctx} 44 } 45 46 func (c *RelationSetCommand) Info() *cmd.Info { 47 return &cmd.Info{ 48 Name: "relation-set", 49 Args: "key=value [key=value ...]", 50 Purpose: "set relation settings", 51 Doc: relationSetDoc, 52 } 53 } 54 55 func (c *RelationSetCommand) SetFlags(f *gnuflag.FlagSet) { 56 rV := newRelationIdValue(c.ctx, &c.RelationId) 57 58 f.Var(rV, "r", "specify a relation by id") 59 f.Var(rV, "relation", "") 60 61 c.settingsFile.SetStdin() 62 f.Var(&c.settingsFile, "file", "file containing key-value pairs") 63 64 f.StringVar(&c.formatFlag, "format", "", "deprecated format flag") 65 } 66 67 func (c *RelationSetCommand) Init(args []string) error { 68 if c.RelationId == -1 { 69 return errors.Errorf("no relation id specified") 70 } 71 72 // The overrides will be applied during Run when c.settingsFile is handled. 73 overrides, err := keyvalues.Parse(args, true) 74 if err != nil { 75 return errors.Trace(err) 76 } 77 c.Settings = overrides 78 return nil 79 } 80 81 func (c *RelationSetCommand) readSettings(in io.Reader) (map[string]string, error) { 82 data, err := ioutil.ReadAll(in) 83 if err != nil { 84 return nil, errors.Trace(err) 85 } 86 87 skipValidation := false // for debugging 88 if !skipValidation { 89 // Can this validation be done more simply or efficiently? 90 91 var scalar string 92 if err := goyaml.Unmarshal(data, &scalar); err != nil { 93 return nil, errors.Trace(err) 94 } 95 if scalar != "" { 96 return nil, errors.Errorf("expected YAML map, got %q", scalar) 97 } 98 99 var sequence []string 100 if err := goyaml.Unmarshal(data, &sequence); err != nil { 101 return nil, errors.Trace(err) 102 } 103 if len(sequence) != 0 { 104 return nil, errors.Errorf("expected YAML map, got %#v", sequence) 105 } 106 } 107 108 kvs := make(map[string]string) 109 if err := goyaml.Unmarshal(data, kvs); err != nil { 110 return nil, errors.Trace(err) 111 } 112 113 return kvs, nil 114 } 115 116 func (c *RelationSetCommand) handleSettingsFile(ctx *cmd.Context) error { 117 if c.settingsFile.Path == "" { 118 return nil 119 } 120 121 file, err := c.settingsFile.Open(ctx) 122 if err != nil { 123 return errors.Trace(err) 124 } 125 defer file.Close() 126 127 settings, err := c.readSettings(file) 128 if err != nil { 129 return errors.Trace(err) 130 } 131 132 overrides := c.Settings 133 for k, v := range overrides { 134 settings[k] = v 135 } 136 c.Settings = settings 137 return nil 138 } 139 140 func (c *RelationSetCommand) Run(ctx *cmd.Context) (err error) { 141 if c.formatFlag != "" { 142 fmt.Fprintf(ctx.Stderr, "--format flag deprecated for command %q", c.Info().Name) 143 } 144 if err := c.handleSettingsFile(ctx); err != nil { 145 return errors.Trace(err) 146 } 147 148 r, found := c.ctx.Relation(c.RelationId) 149 if !found { 150 return fmt.Errorf("unknown relation id") 151 } 152 settings, err := r.Settings() 153 if err != nil { 154 return errors.Annotate(err, "cannot read relation settings") 155 } 156 for k, v := range c.Settings { 157 if v != "" { 158 settings.Set(k, v) 159 } else { 160 settings.Delete(k) 161 } 162 } 163 return nil 164 }