Add a function to load JSON directly

Without this 12 line function it’s impossible to use any of the
Terraform code without the need for having the files on disk. As more
and more people are using (parts of) Terraform in other software, this
seems to be a very welcome addition. It has no negative impact on
Terraform itself whatsoever (the function is never called), but it
opens up a lot of other use cases.

Next to the single new function, I renamed the existing function (and
related tests) to better reflect what the function does. So now there
is a `LoadDir` function which calls `LoadFile` for each file, which
kind of made sense to me, especially when now adding a `LoadJSON`
function as well.

But of course if the rename is a problem, I can revert that part as
it’s not related to the added `LoadJSON` function.

Thanks!
This commit is contained in:
Sander van Harmelen 2015-06-23 16:15:26 +02:00
parent ce8baea6ae
commit c62370f9e9
4 changed files with 95 additions and 27 deletions

View File

@ -450,7 +450,7 @@ func TestVariableDefaultsMap(t *testing.T) {
} }
func testConfig(t *testing.T, name string) *Config { func testConfig(t *testing.T, name string) *Config {
c, err := Load(filepath.Join(fixtureDir, name, "main.tf")) c, err := LoadFile(filepath.Join(fixtureDir, name, "main.tf"))
if err != nil { if err != nil {
t.Fatalf("file: %s\n\nerr: %s", name, err) t.Fatalf("file: %s\n\nerr: %s", name, err)
} }

View File

@ -1,19 +1,41 @@
package config package config
import ( import (
"encoding/json"
"fmt" "fmt"
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
"github.com/hashicorp/hcl"
) )
// Load loads the Terraform configuration from a given file. // LoadJSON loads a single Terraform configuration from a given JSON document.
//
// The document must be a complete Terraform configuration. This function will
// NOT try to load any additional modules so only the given document is loaded.
func LoadJSON(raw json.RawMessage) (*Config, error) {
obj, err := hcl.Parse(string(raw))
if err != nil {
return nil, fmt.Errorf(
"Error parsing JSON document as HCL: %s", err)
}
// Start building the result
hclConfig := &hclConfigurable{
Object: obj,
}
return hclConfig.Config()
}
// LoadFile loads the Terraform configuration from a given file.
// //
// This file can be any format that Terraform recognizes, and import any // This file can be any format that Terraform recognizes, and import any
// other format that Terraform recognizes. // other format that Terraform recognizes.
func Load(path string) (*Config, error) { func LoadFile(path string) (*Config, error) {
importTree, err := loadTree(path) importTree, err := loadTree(path)
if err != nil { if err != nil {
return nil, err return nil, err
@ -66,7 +88,7 @@ func LoadDir(root string) (*Config, error) {
// Load all the regular files, append them to each other. // Load all the regular files, append them to each other.
for _, f := range files { for _, f := range files {
c, err := Load(f) c, err := LoadFile(f)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -83,7 +105,7 @@ func LoadDir(root string) (*Config, error) {
// Load all the overrides, and merge them into the config // Load all the overrides, and merge them into the config
for _, f := range overrides { for _, f := range overrides {
c, err := Load(f) c, err := LoadFile(f)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,6 +1,7 @@
package config package config
import ( import (
"io/ioutil"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings" "strings"
@ -37,15 +38,15 @@ func TestIsEmptyDir_noConfigs(t *testing.T) {
} }
} }
func TestLoad_badType(t *testing.T) { func TestLoadFile_badType(t *testing.T) {
_, err := Load(filepath.Join(fixtureDir, "bad_type.tf.nope")) _, err := LoadFile(filepath.Join(fixtureDir, "bad_type.tf.nope"))
if err == nil { if err == nil {
t.Fatal("should have error") t.Fatal("should have error")
} }
} }
func TestLoadBasic(t *testing.T) { func TestLoadFileBasic(t *testing.T) {
c, err := Load(filepath.Join(fixtureDir, "basic.tf")) c, err := LoadFile(filepath.Join(fixtureDir, "basic.tf"))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -84,8 +85,8 @@ func TestLoadBasic(t *testing.T) {
} }
} }
func TestLoadBasic_empty(t *testing.T) { func TestLoadFileBasic_empty(t *testing.T) {
c, err := Load(filepath.Join(fixtureDir, "empty.tf")) c, err := LoadFile(filepath.Join(fixtureDir, "empty.tf"))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -95,11 +96,11 @@ func TestLoadBasic_empty(t *testing.T) {
} }
} }
func TestLoadBasic_import(t *testing.T) { func TestLoadFileBasic_import(t *testing.T) {
// Skip because we disabled importing // Skip because we disabled importing
t.Skip() t.Skip()
c, err := Load(filepath.Join(fixtureDir, "import.tf")) c, err := LoadFile(filepath.Join(fixtureDir, "import.tf"))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -124,8 +125,8 @@ func TestLoadBasic_import(t *testing.T) {
} }
} }
func TestLoadBasic_json(t *testing.T) { func TestLoadFileBasic_json(t *testing.T) {
c, err := Load(filepath.Join(fixtureDir, "basic.tf.json")) c, err := LoadFile(filepath.Join(fixtureDir, "basic.tf.json"))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -164,8 +165,8 @@ func TestLoadBasic_json(t *testing.T) {
} }
} }
func TestLoadBasic_modules(t *testing.T) { func TestLoadFileBasic_modules(t *testing.T) {
c, err := Load(filepath.Join(fixtureDir, "modules.tf")) c, err := LoadFile(filepath.Join(fixtureDir, "modules.tf"))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -184,8 +185,53 @@ func TestLoadBasic_modules(t *testing.T) {
} }
} }
func TestLoad_variables(t *testing.T) { func TestLoadJSONBasic(t *testing.T) {
c, err := Load(filepath.Join(fixtureDir, "variables.tf")) raw, err := ioutil.ReadFile(filepath.Join(fixtureDir, "basic.tf.json"))
if err != nil {
t.Fatalf("err: %s", err)
}
c, err := LoadJSON(raw)
if err != nil {
t.Fatalf("err: %s", err)
}
if c == nil {
t.Fatal("config should not be nil")
}
if c.Dir != "" {
t.Fatalf("bad: %#v", c.Dir)
}
expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"}
if !reflect.DeepEqual(c.Atlas, expectedAtlas) {
t.Fatalf("bad: %#v", c.Atlas)
}
actual := variablesStr(c.Variables)
if actual != strings.TrimSpace(basicVariablesStr) {
t.Fatalf("bad:\n%s", actual)
}
actual = providerConfigsStr(c.ProviderConfigs)
if actual != strings.TrimSpace(basicProvidersStr) {
t.Fatalf("bad:\n%s", actual)
}
actual = resourcesStr(c.Resources)
if actual != strings.TrimSpace(basicResourcesStr) {
t.Fatalf("bad:\n%s", actual)
}
actual = outputsStr(c.Outputs)
if actual != strings.TrimSpace(basicOutputsStr) {
t.Fatalf("bad:\n%s", actual)
}
}
func TestLoadFile_variables(t *testing.T) {
c, err := LoadFile(filepath.Join(fixtureDir, "variables.tf"))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -303,8 +349,8 @@ func TestLoadDir_override(t *testing.T) {
} }
} }
func TestLoad_provisioners(t *testing.T) { func TestLoadFile_provisioners(t *testing.T) {
c, err := Load(filepath.Join(fixtureDir, "provisioners.tf")) c, err := LoadFile(filepath.Join(fixtureDir, "provisioners.tf"))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -319,8 +365,8 @@ func TestLoad_provisioners(t *testing.T) {
} }
} }
func TestLoad_connections(t *testing.T) { func TestLoadFile_connections(t *testing.T) {
c, err := Load(filepath.Join(fixtureDir, "connection.tf")) c, err := LoadFile(filepath.Join(fixtureDir, "connection.tf"))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -357,8 +403,8 @@ func TestLoad_connections(t *testing.T) {
} }
} }
func TestLoad_createBeforeDestroy(t *testing.T) { func TestLoadFile_createBeforeDestroy(t *testing.T) {
c, err := Load(filepath.Join(fixtureDir, "create-before-destroy.tf")) c, err := LoadFile(filepath.Join(fixtureDir, "create-before-destroy.tf"))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -394,7 +440,7 @@ func TestLoad_createBeforeDestroy(t *testing.T) {
} }
} }
func TestLoad_temporary_files(t *testing.T) { func TestLoadDir_temporary_files(t *testing.T) {
_, err := LoadDir(filepath.Join(fixtureDir, "dir-temporary-files")) _, err := LoadDir(filepath.Join(fixtureDir, "dir-temporary-files"))
if err == nil { if err == nil {
t.Fatalf("Expected to see an error stating no config files found") t.Fatalf("Expected to see an error stating no config files found")

View File

@ -56,7 +56,7 @@ func tempEnv(t *testing.T, k string, v string) string {
} }
func testConfig(t *testing.T, name string) *config.Config { func testConfig(t *testing.T, name string) *config.Config {
c, err := config.Load(filepath.Join(fixtureDir, name, "main.tf")) c, err := config.LoadFile(filepath.Join(fixtureDir, name, "main.tf"))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }