config: refactoring to be less libucl-specific
This commit is contained in:
parent
2ffee2a142
commit
218cc80aab
|
@ -0,0 +1,15 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
// configTree represents a tree of configurations where the root is the
|
||||||
|
// first file and its children are the configurations it has imported.
|
||||||
|
type configTree struct {
|
||||||
|
Path string
|
||||||
|
Config *Config
|
||||||
|
Children []*configTree
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flatten flattens the entire tree down to a single merged Config
|
||||||
|
// structure.
|
||||||
|
func (t *configTree) Flatten() (*Config, error) {
|
||||||
|
return t.Config, nil
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Load loads the Terraform configuration from a given file.
|
||||||
|
func Load(path string) (*Config, error) {
|
||||||
|
importTree, err := loadTree(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
configTree, err := importTree.ConfigTree()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return configTree.Flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type configurable interface {
|
||||||
|
Config() (*Config, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type importTree struct {
|
||||||
|
Path string
|
||||||
|
Raw configurable
|
||||||
|
Children []*importTree
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileLoaderFunc func(path string) (configurable, []string, error)
|
||||||
|
|
||||||
|
func loadTree(root string) (*importTree, error) {
|
||||||
|
c, imps, err := loadFileLibucl(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
children := make([]*importTree, len(imps))
|
||||||
|
for i, imp := range imps {
|
||||||
|
t, err := loadTree(imp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
children[i] = t
|
||||||
|
}
|
||||||
|
|
||||||
|
return &importTree{
|
||||||
|
Path: root,
|
||||||
|
Raw: c,
|
||||||
|
Children: children,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *importTree) ConfigTree() (*configTree, error) {
|
||||||
|
config, err := t.Raw.Config()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"Error loading %s: %s",
|
||||||
|
t.Path,
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build our result
|
||||||
|
result := &configTree{
|
||||||
|
Path: t.Path,
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Follow children and load them
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
|
@ -11,38 +11,39 @@ import (
|
||||||
// equally behaving parsing everywhere.
|
// equally behaving parsing everywhere.
|
||||||
const libuclParseFlags = libucl.ParserKeyLowercase
|
const libuclParseFlags = libucl.ParserKeyLowercase
|
||||||
|
|
||||||
// libuclImportTree represents a tree structure of the imports from the
|
type libuclConfigurable struct {
|
||||||
// configuration files along with the raw libucl objects from those files.
|
Object *libucl.Object
|
||||||
type libuclImportTree struct {
|
|
||||||
Path string
|
|
||||||
Object *libucl.Object
|
|
||||||
Children []*libuclImportTree
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// libuclConfigTree represents a tree structure of the loaded configurations
|
func (t *libuclConfigurable) Config() (*Config, error) {
|
||||||
// of all the Terraform files.
|
var rawConfig struct {
|
||||||
type libuclConfigTree struct {
|
Variable map[string]Variable
|
||||||
Path string
|
}
|
||||||
Config *Config
|
|
||||||
Children []*libuclConfigTree
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load loads the Terraform configuration from a given file.
|
if err := t.Object.Decode(&rawConfig); err != nil {
|
||||||
func Load(path string) (*Config, error) {
|
|
||||||
importTree, err := loadTreeLibucl(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
configTree, err := importTree.ConfigTree()
|
// Start building up the actual configuration. We first
|
||||||
if err != nil {
|
// copy the fields that can be directly assigned.
|
||||||
return nil, err
|
config := new(Config)
|
||||||
|
config.Variables = rawConfig.Variable
|
||||||
|
|
||||||
|
// Build the resources
|
||||||
|
resources := t.Object.Get("resource")
|
||||||
|
if resources != nil {
|
||||||
|
var err error
|
||||||
|
config.Resources, err = loadResourcesLibucl(resources)
|
||||||
|
resources.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return configTree.Config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadTreeLibucl(root string) (*libuclImportTree, error) {
|
func loadFileLibucl(root string) (configurable, []string, error) {
|
||||||
var obj *libucl.Object = nil
|
var obj *libucl.Object = nil
|
||||||
|
|
||||||
// Parse and store the object. We don't use a defer here so that
|
// Parse and store the object. We don't use a defer here so that
|
||||||
|
@ -58,12 +59,11 @@ func loadTreeLibucl(root string) (*libuclImportTree, error) {
|
||||||
|
|
||||||
// If there was an error, return early
|
// If there was an error, return early
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start building the result
|
// Start building the result
|
||||||
result := &libuclImportTree{
|
result := &libuclConfigurable{
|
||||||
Path: root,
|
|
||||||
Object: obj,
|
Object: obj,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,13 +71,13 @@ func loadTreeLibucl(root string) (*libuclImportTree, error) {
|
||||||
imports := obj.Get("import")
|
imports := obj.Get("import")
|
||||||
if imports == nil {
|
if imports == nil {
|
||||||
result.Object.Ref()
|
result.Object.Ref()
|
||||||
return result, nil
|
return result, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if imports.Type() != libucl.ObjectTypeString {
|
if imports.Type() != libucl.ObjectTypeString {
|
||||||
imports.Close()
|
imports.Close()
|
||||||
|
|
||||||
return nil, fmt.Errorf(
|
return nil, nil, fmt.Errorf(
|
||||||
"Error in %s: all 'import' declarations should be in the format\n"+
|
"Error in %s: all 'import' declarations should be in the format\n"+
|
||||||
"`import \"foo\"` (Got type %s)",
|
"`import \"foo\"` (Got type %s)",
|
||||||
root,
|
root,
|
||||||
|
@ -88,31 +88,21 @@ func loadTreeLibucl(root string) (*libuclImportTree, error) {
|
||||||
importPaths := make([]string, 0, imports.Len())
|
importPaths := make([]string, 0, imports.Len())
|
||||||
iter := imports.Iterate(false)
|
iter := imports.Iterate(false)
|
||||||
for imp := iter.Next(); imp != nil; imp = iter.Next() {
|
for imp := iter.Next(); imp != nil; imp = iter.Next() {
|
||||||
importPaths = append(importPaths, imp.ToString())
|
path := imp.ToString()
|
||||||
imp.Close()
|
|
||||||
}
|
|
||||||
iter.Close()
|
|
||||||
imports.Close()
|
|
||||||
|
|
||||||
// Load them all
|
|
||||||
result.Children = make([]*libuclImportTree, len(importPaths))
|
|
||||||
for i, path := range importPaths {
|
|
||||||
if !filepath.IsAbs(path) {
|
if !filepath.IsAbs(path) {
|
||||||
// Relative paths are relative to the Terraform file itself
|
// Relative paths are relative to the Terraform file itself
|
||||||
dir := filepath.Dir(root)
|
dir := filepath.Dir(root)
|
||||||
path = filepath.Join(dir, path)
|
path = filepath.Join(dir, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
imp, err := loadTreeLibucl(path)
|
importPaths = append(importPaths, path)
|
||||||
if err != nil {
|
imp.Close()
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Children[i] = imp
|
|
||||||
}
|
}
|
||||||
|
iter.Close()
|
||||||
|
imports.Close()
|
||||||
|
|
||||||
result.Object.Ref()
|
result.Object.Ref()
|
||||||
return result, nil
|
return result, importPaths, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a handle to a libucl object, this recurses into the structure
|
// Given a handle to a libucl object, this recurses into the structure
|
||||||
|
@ -188,60 +178,3 @@ func loadResourcesLibucl(o *libucl.Object) ([]Resource, error) {
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *libuclImportTree) ConfigTree() (*libuclConfigTree, error) {
|
|
||||||
var rawConfig struct {
|
|
||||||
Variable map[string]Variable
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := t.Object.Decode(&rawConfig); err != nil {
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"Error decoding %s: %s",
|
|
||||||
t.Path,
|
|
||||||
err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start building up the actual configuration. We first
|
|
||||||
// copy the fields that can be directly assigned.
|
|
||||||
config := new(Config)
|
|
||||||
config.Variables = rawConfig.Variable
|
|
||||||
|
|
||||||
// Build the resources
|
|
||||||
resources := t.Object.Get("resource")
|
|
||||||
if resources != nil {
|
|
||||||
var err error
|
|
||||||
config.Resources, err = loadResourcesLibucl(resources)
|
|
||||||
resources.Close()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build our result
|
|
||||||
result := &libuclConfigTree{
|
|
||||||
Path: t.Path,
|
|
||||||
Config: config,
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper for parsing a single libucl-formatted file into
|
|
||||||
// the given structure.
|
|
||||||
func parseFile(path string, result interface{}) error {
|
|
||||||
parser := libucl.NewParser(libuclParseFlags)
|
|
||||||
defer parser.Close()
|
|
||||||
|
|
||||||
if err := parser.AddFile(path); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
root := parser.Object()
|
|
||||||
defer root.Close()
|
|
||||||
|
|
||||||
if err := root.Decode(result); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue