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  }