Skip to content

Commit

Permalink
Add TypeKVPairs field type (hashicorp#3535)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrishoffman authored Nov 7, 2017
1 parent 2994b26 commit 9265035
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 2 deletions.
2 changes: 2 additions & 0 deletions logical/framework/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,8 @@ func (t FieldType) Zero() interface{} {
return false
case TypeMap:
return map[string]interface{}{}
case TypeKVPairs:
return map[string]string{}
case TypeDurationSecond:
return 0
case TypeSlice:
Expand Down
30 changes: 28 additions & 2 deletions logical/framework/field_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"regexp"
"strings"

"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/helper/strutil"
Expand Down Expand Up @@ -34,7 +35,8 @@ func (d *FieldData) Validate() error {

switch schema.Type {
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeString,
TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice:
TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
TypeKVPairs:
_, _, err := d.getPrimitive(field, schema)
if err != nil {
return fmt.Errorf("Error converting input %v for field %s: %s", value, field, err)
Expand Down Expand Up @@ -110,7 +112,8 @@ func (d *FieldData) GetOkErr(k string) (interface{}, bool, error) {

switch schema.Type {
case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeString,
TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice:
TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice,
TypeKVPairs:
return d.getPrimitive(k, schema)
default:
return nil, false,
Expand Down Expand Up @@ -236,6 +239,29 @@ func (d *FieldData) getPrimitive(
}
return strutil.TrimStrings(result), true, nil

case TypeKVPairs:
// First try to parse this as a map
var mapResult map[string]string
if err := mapstructure.WeakDecode(raw, &mapResult); err == nil {
return mapResult, true, nil
}

// If map parse fails, parse as a string list of = delimited pairs
var listResult []string
if err := mapstructure.WeakDecode(raw, &listResult); err != nil {
return nil, true, err
}

result := make(map[string]string, len(listResult))
for _, keyPair := range listResult {
keyPairSlice := strings.SplitN(keyPair, "=", 2)
if len(keyPairSlice) != 2 || keyPairSlice[0] == "" {
return nil, false, fmt.Errorf("invalid key pair %q", keyPair)
}
result[keyPairSlice[0]] = keyPairSlice[1]
}
return result, true, nil

default:
panic(fmt.Sprintf("Unknown type: %s", schema.Type))
}
Expand Down
65 changes: 65 additions & 0 deletions logical/framework/field_data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,53 @@ func TestFieldDataGet(t *testing.T) {
"bar.baz-bay123",
},

"keypair type, valid value map type": {
map[string]*FieldSchema{
"foo": &FieldSchema{Type: TypeKVPairs},
},
map[string]interface{}{
"foo": map[string]interface{}{
"key1": "value1",
"key2": "value2",
"key3": 1,
},
},
"foo",
map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "1",
},
},

"keypair type, list of equal sign delim key pairs type": {
map[string]*FieldSchema{
"foo": &FieldSchema{Type: TypeKVPairs},
},
map[string]interface{}{
"foo": []interface{}{"key1=value1", "key2=value2", "key3=1"},
},
"foo",
map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "1",
},
},

"keypair type, single equal sign delim value": {
map[string]*FieldSchema{
"foo": &FieldSchema{Type: TypeKVPairs},
},
map[string]interface{}{
"foo": "key1=value1",
},
"foo",
map[string]string{
"key1": "value1",
},
},

"name string type, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeNameString},
Expand Down Expand Up @@ -359,6 +406,15 @@ func TestFieldDataGet(t *testing.T) {
"foo",
[]string{},
},

"type kv pair, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeKVPairs},
},
map[string]interface{}{},
"foo",
map[string]string{},
},
}

for name, tc := range cases {
Expand Down Expand Up @@ -422,6 +478,15 @@ func TestFieldDataGet_Error(t *testing.T) {
},
"foo",
},
"keypair type, csv version empty key name": {
map[string]*FieldSchema{
"foo": &FieldSchema{Type: TypeKVPairs},
},
map[string]interface{}{
"foo": []interface{}{"=value1", "key2=value2", "key3=1"},
},
"foo",
},
}

for _, tc := range cases {
Expand Down
6 changes: 6 additions & 0 deletions logical/framework/field_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ const (
// rules. These rules include start and end with an alphanumeric
// character and characters in the middle can be alphanumeric or . or -.
TypeNameString

// TypeKVPairs allows you to represent the data as a map or a list of
// equal sign delimited key pairs
TypeKVPairs
)

func (t FieldType) String() string {
Expand All @@ -44,6 +48,8 @@ func (t FieldType) String() string {
return "bool"
case TypeMap:
return "map"
case TypeKVPairs:
return "keypair"
case TypeDurationSecond:
return "duration (sec)"
case TypeSlice, TypeStringSlice, TypeCommaStringSlice:
Expand Down

0 comments on commit 9265035

Please sign in to comment.