package command
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/command/base"
)
// KVImportCommand is a Command implementation that is used to import
// a KV tree stored as JSON
type KVImportCommand struct {
base . Command
// testStdin is the input for testing.
testStdin io . Reader
}
func ( c * KVImportCommand ) Synopsis ( ) string {
return "Imports a tree stored as JSON to the KV store"
}
func ( c * KVImportCommand ) Help ( ) string {
helpText := `
Usage : consul kv import [ DATA ]
Imports key - value pairs to the key - value store from the JSON representation
generated by the "consul kv export" command .
The data can be read from a file by prefixing the filename with the "@"
symbol . For example :
$ consul kv import @ filename . json
Or it can be read from stdin using the "-" symbol :
$ cat filename . json | consul kv import config / program / license -
Alternatively the data may be provided as the final parameter to the command ,
though care must be taken with regards to shell escaping .
For a full list of options and examples , please see the Consul documentation .
` + c . Command . Help ( )
return strings . TrimSpace ( helpText )
}
func ( c * KVImportCommand ) Run ( args [ ] string ) int {
f := c . Command . NewFlagSet ( c )
if err := c . Command . Parse ( args ) ; err != nil {
return 1
}
// Check for arg validation
args = f . Args ( )
data , err := c . dataFromArgs ( args )
if err != nil {
c . Ui . Error ( fmt . Sprintf ( "Error! %s" , err ) )
return 1
}
// Create and test the HTTP client
client , err := c . Command . HTTPClient ( )
if err != nil {
c . Ui . Error ( fmt . Sprintf ( "Error connecting to Consul agent: %s" , err ) )
return 1
}
var entries [ ] * kvExportEntry
if err := json . Unmarshal ( [ ] byte ( data ) , & entries ) ; err != nil {
c . Ui . Error ( fmt . Sprintf ( "Cannot unmarshal data: %s" , err ) )
return 1
}
for _ , entry := range entries {
value , err := base64 . StdEncoding . DecodeString ( entry . Value )
if err != nil {
c . Ui . Error ( fmt . Sprintf ( "Error base 64 decoding value for key %s: %s" , entry . Key , err ) )
return 1
}
pair := & api . KVPair {
Key : entry . Key ,
Flags : entry . Flags ,
Value : value ,
}
if _ , err := client . KV ( ) . Put ( pair , nil ) ; err != nil {
c . Ui . Error ( fmt . Sprintf ( "Error! Failed writing data for key %s: %s" , pair . Key , err ) )
return 1
}
c . Ui . Info ( fmt . Sprintf ( "Imported: %s" , pair . Key ) )
}
return 0
}
func ( c * KVImportCommand ) dataFromArgs ( args [ ] string ) ( string , error ) {
var stdin io . Reader = os . Stdin
if c . testStdin != nil {
stdin = c . testStdin
}
switch len ( args ) {
case 0 :
return "" , errors . New ( "Missing DATA argument" )
case 1 :
default :
return "" , fmt . Errorf ( "Too many arguments (expected 1 or 2, got %d)" , len ( args ) )
}
data := args [ 0 ]
if len ( data ) == 0 {
return "" , errors . New ( "Empty DATA argument" )
}
switch data [ 0 ] {
case '@' :
data , err := ioutil . ReadFile ( data [ 1 : ] )
if err != nil {
return "" , fmt . Errorf ( "Failed to read file: %s" , err )
}
return string ( data ) , nil
case '-' :
if len ( data ) > 1 {
return data , nil
} else {
var b bytes . Buffer
if _ , err := io . Copy ( & b , stdin ) ; err != nil {
return "" , fmt . Errorf ( "Failed to read stdin: %s" , err )
}
return b . String ( ) , nil
}
default :
return data , nil
}
}