Parse HCL keys in command config

This commit is contained in:
Seth Vargo 2016-03-10 13:36:17 -05:00
parent f916ed349d
commit b817b60183
2 changed files with 83 additions and 12 deletions

View File

@ -5,7 +5,9 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/hcl" "github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
) )
@ -44,22 +46,65 @@ func LoadConfig(path string) (*Config, error) {
return nil, fmt.Errorf("Error expanding config path: %s", err) return nil, fmt.Errorf("Error expanding config path: %s", err)
} }
var config Config
contents, err := ioutil.ReadFile(path) contents, err := ioutil.ReadFile(path)
if !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
if err != nil { return nil, err
return nil, err }
}
obj, err := hcl.Parse(string(contents)) return ParseConfig(string(contents))
if err != nil { }
return nil, err
}
if err := hcl.DecodeObject(&config, obj); err != nil { // ParseConfig parses the given configuration as a string.
return nil, err func ParseConfig(contents string) (*Config, error) {
root, err := hcl.Parse(contents)
if err != nil {
return nil, err
}
// Top-level item should be the object list
list, ok := root.Node.(*ast.ObjectList)
if !ok {
return nil, fmt.Errorf("Failed to parse config: does not contain a root object")
}
valid := []string{
"token_helper",
}
if err := checkHCLKeys(list, valid); err != nil {
return nil, err
}
var c Config
if err := hcl.DecodeObject(&c, list); err != nil {
return nil, err
}
return &c, nil
}
func checkHCLKeys(node ast.Node, valid []string) error {
var list *ast.ObjectList
switch n := node.(type) {
case *ast.ObjectList:
list = n
case *ast.ObjectType:
list = n.List
default:
return fmt.Errorf("cannot check HCL keys of type %T", n)
}
validMap := make(map[string]struct{}, len(valid))
for _, v := range valid {
validMap[v] = struct{}{}
}
var result error
for _, item := range list.Items {
key := item.Keys[0].Token.Value().(string)
if _, ok := validMap[key]; !ok {
result = multierror.Append(result, fmt.Errorf(
"invalid key '%s' on line %d", key, item.Assign.Line))
} }
} }
return &config, nil return result
} }

View File

@ -3,6 +3,7 @@ package command
import ( import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings"
"testing" "testing"
) )
@ -19,3 +20,28 @@ func TestLoadConfig(t *testing.T) {
t.Fatalf("bad: %#v", config) t.Fatalf("bad: %#v", config)
} }
} }
func TestLoadConfig_noExist(t *testing.T) {
config, err := LoadConfig("nope/not-once/.never")
if err != nil {
t.Fatal(err)
}
if config.TokenHelper != "" {
t.Errorf("expected %q to be %q", config.TokenHelper, "")
}
}
func TestParseConfig_badKeys(t *testing.T) {
_, err := ParseConfig(`
token_helper = "/token"
nope = "true"
`)
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "invalid key 'nope' on line 3") {
t.Errorf("bad error: %s", err.Error())
}
}