github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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.v2"
    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  	kvs := make(map[string]string)
    95  	if err := goyaml.Unmarshal(data, kvs); err != nil {
    96  		return nil, errors.Trace(err)
    97  	}
    98  
    99  	return kvs, nil
   100  }
   101  
   102  func (c *RelationSetCommand) handleSettingsFile(ctx *cmd.Context) error {
   103  	if c.settingsFile.Path == "" {
   104  		return nil
   105  	}
   106  
   107  	file, err := c.settingsFile.Open(ctx)
   108  	if err != nil {
   109  		return errors.Trace(err)
   110  	}
   111  	defer file.Close()
   112  
   113  	settings, err := c.readSettings(file)
   114  	if err != nil {
   115  		return errors.Trace(err)
   116  	}
   117  
   118  	overrides := c.Settings
   119  	for k, v := range overrides {
   120  		settings[k] = v
   121  	}
   122  	c.Settings = settings
   123  	return nil
   124  }
   125  
   126  func (c *RelationSetCommand) Run(ctx *cmd.Context) (err error) {
   127  	if c.formatFlag != "" {
   128  		fmt.Fprintf(ctx.Stderr, "--format flag deprecated for command %q", c.Info().Name)
   129  	}
   130  	if err := c.handleSettingsFile(ctx); err != nil {
   131  		return errors.Trace(err)
   132  	}
   133  
   134  	r, err := c.ctx.Relation(c.RelationId)
   135  	if err != nil {
   136  		return errors.Trace(err)
   137  	}
   138  	settings, err := r.Settings()
   139  	if err != nil {
   140  		return errors.Annotate(err, "cannot read relation settings")
   141  	}
   142  	for k, v := range c.Settings {
   143  		if v != "" {
   144  			settings.Set(k, v)
   145  		} else {
   146  			settings.Delete(k)
   147  		}
   148  	}
   149  	return nil
   150  }