github.com/sl1pm4t/consul@v1.4.5-0.20190325224627-74c31c540f9c/command/kv/put/kv_put.go (about)

     1  package put
     2  
     3  import (
     4  	"encoding/base64"
     5  	"flag"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/hashicorp/consul/api"
    10  	"github.com/hashicorp/consul/command/flags"
    11  	"github.com/hashicorp/consul/command/helpers"
    12  	"github.com/mitchellh/cli"
    13  )
    14  
    15  func New(ui cli.Ui) *cmd {
    16  	c := &cmd{UI: ui}
    17  	c.init()
    18  	return c
    19  }
    20  
    21  type cmd struct {
    22  	UI    cli.Ui
    23  	flags *flag.FlagSet
    24  	http  *flags.HTTPFlags
    25  	help  string
    26  
    27  	// flags
    28  	cas           bool
    29  	kvflags       uint64
    30  	base64encoded bool
    31  	modifyIndex   uint64
    32  	session       string
    33  	acquire       bool
    34  	release       bool
    35  
    36  	// testStdin is the input for testing.
    37  	testStdin io.Reader
    38  }
    39  
    40  func (c *cmd) init() {
    41  	c.flags = flag.NewFlagSet("", flag.ContinueOnError)
    42  	c.flags.BoolVar(&c.cas, "cas", false,
    43  		"Perform a Check-And-Set operation. Specifying this value also "+
    44  			"requires the -modify-index flag to be set. The default value "+
    45  			"is false.")
    46  	c.flags.Uint64Var(&c.kvflags, "flags", 0,
    47  		"Unsigned integer value to assign to this key-value pair. This "+
    48  			"value is not read by Consul, so clients can use this value however "+
    49  			"makes sense for their use case. The default value is 0 (no flags).")
    50  	c.flags.BoolVar(&c.base64encoded, "base64", false,
    51  		"Treat the data as base 64 encoded. The default value is false.")
    52  	c.flags.Uint64Var(&c.modifyIndex, "modify-index", 0,
    53  		"Unsigned integer representing the ModifyIndex of the key. This is "+
    54  			"used in combination with the -cas flag.")
    55  	c.flags.StringVar(&c.session, "session", "",
    56  		"User-defined identifer for this session as a string. This is commonly "+
    57  			"used with the -acquire and -release operations to build robust locking, "+
    58  			"but it can be set on any key. The default value is empty (no session).")
    59  	c.flags.BoolVar(&c.acquire, "acquire", false,
    60  		"Obtain a lock on the key. If the key does not exist, this operation "+
    61  			"will create the key and obtain the lock. The session must already "+
    62  			"exist and be specified via the -session flag. The default value is false.")
    63  	c.flags.BoolVar(&c.release, "release", false,
    64  		"Forfeit the lock on the key at the given path. This requires the "+
    65  			"-session flag to be set. The key must be held by the session in order to "+
    66  			"be unlocked. The default value is false.")
    67  
    68  	c.http = &flags.HTTPFlags{}
    69  	flags.Merge(c.flags, c.http.ClientFlags())
    70  	flags.Merge(c.flags, c.http.ServerFlags())
    71  	c.help = flags.Usage(help, c.flags)
    72  }
    73  
    74  func (c *cmd) Run(args []string) int {
    75  	if err := c.flags.Parse(args); err != nil {
    76  		return 1
    77  	}
    78  
    79  	// Check for arg validation
    80  	args = c.flags.Args()
    81  	key, data, err := c.dataFromArgs(args)
    82  	if err != nil {
    83  		c.UI.Error(fmt.Sprintf("Error! %s", err))
    84  		return 1
    85  	}
    86  
    87  	dataBytes := []byte(data)
    88  	if c.base64encoded {
    89  		dataBytes, err = base64.StdEncoding.DecodeString(data)
    90  		if err != nil {
    91  			c.UI.Error(fmt.Sprintf("Error! Cannot base 64 decode data: %s", err))
    92  		}
    93  	}
    94  
    95  	// Session is required for release or acquire
    96  	if (c.release || c.acquire) && c.session == "" {
    97  		c.UI.Error("Error! Missing -session (required with -acquire and -release)")
    98  		return 1
    99  	}
   100  
   101  	// ModifyIndex is required for CAS
   102  	if c.cas && c.modifyIndex == 0 {
   103  		c.UI.Error("Must specify -modify-index with -cas!")
   104  		return 1
   105  	}
   106  
   107  	// Create and test the HTTP client
   108  	client, err := c.http.APIClient()
   109  	if err != nil {
   110  		c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
   111  		return 1
   112  	}
   113  
   114  	pair := &api.KVPair{
   115  		Key:         key,
   116  		ModifyIndex: c.modifyIndex,
   117  		Flags:       c.kvflags,
   118  		Value:       dataBytes,
   119  		Session:     c.session,
   120  	}
   121  
   122  	switch {
   123  	case c.cas:
   124  		ok, _, err := client.KV().CAS(pair, nil)
   125  		if err != nil {
   126  			c.UI.Error(fmt.Sprintf("Error! Did not write to %s: %s", key, err))
   127  			return 1
   128  		}
   129  		if !ok {
   130  			c.UI.Error(fmt.Sprintf("Error! Did not write to %s: CAS failed", key))
   131  			return 1
   132  		}
   133  
   134  		c.UI.Info(fmt.Sprintf("Success! Data written to: %s", key))
   135  		return 0
   136  	case c.acquire:
   137  		ok, _, err := client.KV().Acquire(pair, nil)
   138  		if err != nil {
   139  			c.UI.Error(fmt.Sprintf("Error! Failed writing data: %s", err))
   140  			return 1
   141  		}
   142  		if !ok {
   143  			c.UI.Error("Error! Did not acquire lock")
   144  			return 1
   145  		}
   146  
   147  		c.UI.Info(fmt.Sprintf("Success! Lock acquired on: %s", key))
   148  		return 0
   149  	case c.release:
   150  		ok, _, err := client.KV().Release(pair, nil)
   151  		if err != nil {
   152  			c.UI.Error(fmt.Sprintf("Error! Failed writing data: %s", key))
   153  			return 1
   154  		}
   155  		if !ok {
   156  			c.UI.Error("Error! Did not release lock")
   157  			return 1
   158  		}
   159  
   160  		c.UI.Info(fmt.Sprintf("Success! Lock released on: %s", key))
   161  		return 0
   162  	default:
   163  		if _, err := client.KV().Put(pair, nil); err != nil {
   164  			c.UI.Error(fmt.Sprintf("Error! Failed writing data: %s", err))
   165  			return 1
   166  		}
   167  
   168  		c.UI.Info(fmt.Sprintf("Success! Data written to: %s", key))
   169  		return 0
   170  	}
   171  }
   172  
   173  func (c *cmd) dataFromArgs(args []string) (string, string, error) {
   174  	switch len(args) {
   175  	case 0:
   176  		return "", "", fmt.Errorf("Missing KEY argument")
   177  	case 1:
   178  		return args[0], "", nil
   179  	case 2:
   180  	default:
   181  		return "", "", fmt.Errorf("Too many arguments (expected 1 or 2, got %d)", len(args))
   182  	}
   183  
   184  	key := args[0]
   185  	data, err := helpers.LoadDataSource(args[1], c.testStdin)
   186  
   187  	if err != nil {
   188  		return "", "", err
   189  	} else {
   190  		return key, data, nil
   191  	}
   192  }
   193  
   194  func (c *cmd) Synopsis() string {
   195  	return synopsis
   196  }
   197  
   198  func (c *cmd) Help() string {
   199  	return c.help
   200  }
   201  
   202  const synopsis = "Sets or updates data in the KV store"
   203  const help = `
   204  Usage: consul kv put [options] KEY [DATA]
   205  
   206    Writes the data to the given path in the key-value store. The data can be of
   207    any type.
   208  
   209        $ consul kv put config/redis/maxconns 5
   210  
   211    The data can also be consumed from a file on disk by prefixing with the "@"
   212    symbol. For example:
   213  
   214        $ consul kv put config/program/license @license.lic
   215  
   216    Or it can be read from stdin using the "-" symbol:
   217  
   218        $ echo "abcd1234" | consul kv put config/program/license -
   219  
   220    The DATA argument itself is optional. If omitted, this will create an empty
   221    key-value pair at the specified path:
   222  
   223        $ consul kv put webapp/beta/active
   224  
   225    If the -base64 flag is specified, the data will be treated as base 64
   226    encoded.
   227  
   228    To perform a Check-And-Set operation, specify the -cas flag with the
   229    appropriate -modify-index flag corresponding to the key you want to perform
   230    the CAS operation on:
   231  
   232        $ consul kv put -cas -modify-index=844 config/redis/maxconns 5
   233  
   234    Additional flags and more advanced use cases are detailed below.
   235  `