terraform/internal/configs/configload/loader_snapshot_test.go

122 lines
3.3 KiB
Go
Raw Permalink Normal View History

configload: Configuration snapshots Here we introduce a new idea of a "configuration snapshot", which is an in-memory copy of the source code of each of the files that make up the configuration. The primary intended purpose for this is as an intermediate step before writing the configuration files into a plan file, and then reading them out when that plan file is later applied. During earlier configs package development we expected to use an afero vfs implementation to read directly from the zip file, but that doesn't work in practice because we need to preserve module paths from the source file system that might include parent directory traversals (../) while retaining the original path for use in error messages. The result, for now, is a bit of an abstraction inversion: we implement a specialized afero vfs implementation that makes the sparse filesystem representation from a snapshot appear like a normal filesystem just well enough that the config loader and parser can work with it. In future we may wish to rework the internals here so that the main abstraction is at a similar level to the snapshot and then that API is mapped to the native filesystem in the normal case, removing afero. For now though, this approach avoids the need for a significant redesign of the parser/loader internals, at the expense of some trickiness in the case where we're reading from a snapshot. This commit does not yet include the reading and writing of snapshots into plan files. That will follow in a subsequent commit.
2018-06-16 02:23:53 +02:00
package configload
import (
"path/filepath"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/go-test/deep"
)
func TestLoadConfigWithSnapshot(t *testing.T) {
fixtureDir := filepath.Clean("testdata/already-installed")
configload: Configuration snapshots Here we introduce a new idea of a "configuration snapshot", which is an in-memory copy of the source code of each of the files that make up the configuration. The primary intended purpose for this is as an intermediate step before writing the configuration files into a plan file, and then reading them out when that plan file is later applied. During earlier configs package development we expected to use an afero vfs implementation to read directly from the zip file, but that doesn't work in practice because we need to preserve module paths from the source file system that might include parent directory traversals (../) while retaining the original path for use in error messages. The result, for now, is a bit of an abstraction inversion: we implement a specialized afero vfs implementation that makes the sparse filesystem representation from a snapshot appear like a normal filesystem just well enough that the config loader and parser can work with it. In future we may wish to rework the internals here so that the main abstraction is at a similar level to the snapshot and then that API is mapped to the native filesystem in the normal case, removing afero. For now though, this approach avoids the need for a significant redesign of the parser/loader internals, at the expense of some trickiness in the case where we're reading from a snapshot. This commit does not yet include the reading and writing of snapshots into plan files. That will follow in a subsequent commit.
2018-06-16 02:23:53 +02:00
loader, err := NewLoader(&Config{
ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"),
})
if err != nil {
t.Fatalf("unexpected error from NewLoader: %s", err)
}
_, got, diags := loader.LoadConfigWithSnapshot(fixtureDir)
assertNoDiagnostics(t, diags)
if got == nil {
t.Fatalf("snapshot is nil; want non-nil")
}
t.Log(spew.Sdump(got))
{
gotModuleDirs := map[string]string{}
for k, m := range got.Modules {
gotModuleDirs[k] = m.Dir
}
wantModuleDirs := map[string]string{
"": "testdata/already-installed",
"child_a": "testdata/already-installed/.terraform/modules/child_a",
"child_a.child_c": "testdata/already-installed/.terraform/modules/child_a/child_c",
"child_b": "testdata/already-installed/.terraform/modules/child_b",
"child_b.child_d": "testdata/already-installed/.terraform/modules/child_b.child_d",
configload: Configuration snapshots Here we introduce a new idea of a "configuration snapshot", which is an in-memory copy of the source code of each of the files that make up the configuration. The primary intended purpose for this is as an intermediate step before writing the configuration files into a plan file, and then reading them out when that plan file is later applied. During earlier configs package development we expected to use an afero vfs implementation to read directly from the zip file, but that doesn't work in practice because we need to preserve module paths from the source file system that might include parent directory traversals (../) while retaining the original path for use in error messages. The result, for now, is a bit of an abstraction inversion: we implement a specialized afero vfs implementation that makes the sparse filesystem representation from a snapshot appear like a normal filesystem just well enough that the config loader and parser can work with it. In future we may wish to rework the internals here so that the main abstraction is at a similar level to the snapshot and then that API is mapped to the native filesystem in the normal case, removing afero. For now though, this approach avoids the need for a significant redesign of the parser/loader internals, at the expense of some trickiness in the case where we're reading from a snapshot. This commit does not yet include the reading and writing of snapshots into plan files. That will follow in a subsequent commit.
2018-06-16 02:23:53 +02:00
}
problems := deep.Equal(wantModuleDirs, gotModuleDirs)
for _, problem := range problems {
t.Errorf(problem)
}
if len(problems) > 0 {
return
}
}
gotRoot := got.Modules[""]
wantRoot := &SnapshotModule{
Dir: "testdata/already-installed",
configload: Configuration snapshots Here we introduce a new idea of a "configuration snapshot", which is an in-memory copy of the source code of each of the files that make up the configuration. The primary intended purpose for this is as an intermediate step before writing the configuration files into a plan file, and then reading them out when that plan file is later applied. During earlier configs package development we expected to use an afero vfs implementation to read directly from the zip file, but that doesn't work in practice because we need to preserve module paths from the source file system that might include parent directory traversals (../) while retaining the original path for use in error messages. The result, for now, is a bit of an abstraction inversion: we implement a specialized afero vfs implementation that makes the sparse filesystem representation from a snapshot appear like a normal filesystem just well enough that the config loader and parser can work with it. In future we may wish to rework the internals here so that the main abstraction is at a similar level to the snapshot and then that API is mapped to the native filesystem in the normal case, removing afero. For now though, this approach avoids the need for a significant redesign of the parser/loader internals, at the expense of some trickiness in the case where we're reading from a snapshot. This commit does not yet include the reading and writing of snapshots into plan files. That will follow in a subsequent commit.
2018-06-16 02:23:53 +02:00
Files: map[string][]byte{
"root.tf": []byte(`
module "child_a" {
source = "example.com/foo/bar_a/baz"
version = ">= 1.0.0"
}
module "child_b" {
source = "example.com/foo/bar_b/baz"
version = ">= 1.0.0"
}
`),
},
}
if !reflect.DeepEqual(gotRoot, wantRoot) {
t.Errorf("wrong root module snapshot\ngot: %swant: %s", spew.Sdump(gotRoot), spew.Sdump(wantRoot))
}
}
func TestSnapshotRoundtrip(t *testing.T) {
fixtureDir := filepath.Clean("testdata/already-installed")
configload: Configuration snapshots Here we introduce a new idea of a "configuration snapshot", which is an in-memory copy of the source code of each of the files that make up the configuration. The primary intended purpose for this is as an intermediate step before writing the configuration files into a plan file, and then reading them out when that plan file is later applied. During earlier configs package development we expected to use an afero vfs implementation to read directly from the zip file, but that doesn't work in practice because we need to preserve module paths from the source file system that might include parent directory traversals (../) while retaining the original path for use in error messages. The result, for now, is a bit of an abstraction inversion: we implement a specialized afero vfs implementation that makes the sparse filesystem representation from a snapshot appear like a normal filesystem just well enough that the config loader and parser can work with it. In future we may wish to rework the internals here so that the main abstraction is at a similar level to the snapshot and then that API is mapped to the native filesystem in the normal case, removing afero. For now though, this approach avoids the need for a significant redesign of the parser/loader internals, at the expense of some trickiness in the case where we're reading from a snapshot. This commit does not yet include the reading and writing of snapshots into plan files. That will follow in a subsequent commit.
2018-06-16 02:23:53 +02:00
loader, err := NewLoader(&Config{
ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"),
})
if err != nil {
t.Fatalf("unexpected error from NewLoader: %s", err)
}
_, snap, diags := loader.LoadConfigWithSnapshot(fixtureDir)
assertNoDiagnostics(t, diags)
if snap == nil {
t.Fatalf("snapshot is nil; want non-nil")
}
snapLoader := NewLoaderFromSnapshot(snap)
if loader == nil {
t.Fatalf("loader is nil; want non-nil")
}
config, diags := snapLoader.LoadConfig(fixtureDir)
assertNoDiagnostics(t, diags)
if config == nil {
t.Fatalf("config is nil; want non-nil")
}
if config.Module == nil {
t.Fatalf("config has no root module")
}
if got, want := config.Module.SourceDir, "testdata/already-installed"; got != want {
configload: Configuration snapshots Here we introduce a new idea of a "configuration snapshot", which is an in-memory copy of the source code of each of the files that make up the configuration. The primary intended purpose for this is as an intermediate step before writing the configuration files into a plan file, and then reading them out when that plan file is later applied. During earlier configs package development we expected to use an afero vfs implementation to read directly from the zip file, but that doesn't work in practice because we need to preserve module paths from the source file system that might include parent directory traversals (../) while retaining the original path for use in error messages. The result, for now, is a bit of an abstraction inversion: we implement a specialized afero vfs implementation that makes the sparse filesystem representation from a snapshot appear like a normal filesystem just well enough that the config loader and parser can work with it. In future we may wish to rework the internals here so that the main abstraction is at a similar level to the snapshot and then that API is mapped to the native filesystem in the normal case, removing afero. For now though, this approach avoids the need for a significant redesign of the parser/loader internals, at the expense of some trickiness in the case where we're reading from a snapshot. This commit does not yet include the reading and writing of snapshots into plan files. That will follow in a subsequent commit.
2018-06-16 02:23:53 +02:00
t.Errorf("wrong root module sourcedir %q; want %q", got, want)
}
if got, want := len(config.Module.ModuleCalls), 2; got != want {
t.Errorf("wrong number of module calls in root module %d; want %d", got, want)
}
childA := config.Children["child_a"]
if childA == nil {
t.Fatalf("child_a config is nil; want non-nil")
}
if childA.Module == nil {
t.Fatalf("child_a config has no module")
}
if got, want := childA.Module.SourceDir, "testdata/already-installed/.terraform/modules/child_a"; got != want {
configload: Configuration snapshots Here we introduce a new idea of a "configuration snapshot", which is an in-memory copy of the source code of each of the files that make up the configuration. The primary intended purpose for this is as an intermediate step before writing the configuration files into a plan file, and then reading them out when that plan file is later applied. During earlier configs package development we expected to use an afero vfs implementation to read directly from the zip file, but that doesn't work in practice because we need to preserve module paths from the source file system that might include parent directory traversals (../) while retaining the original path for use in error messages. The result, for now, is a bit of an abstraction inversion: we implement a specialized afero vfs implementation that makes the sparse filesystem representation from a snapshot appear like a normal filesystem just well enough that the config loader and parser can work with it. In future we may wish to rework the internals here so that the main abstraction is at a similar level to the snapshot and then that API is mapped to the native filesystem in the normal case, removing afero. For now though, this approach avoids the need for a significant redesign of the parser/loader internals, at the expense of some trickiness in the case where we're reading from a snapshot. This commit does not yet include the reading and writing of snapshots into plan files. That will follow in a subsequent commit.
2018-06-16 02:23:53 +02:00
t.Errorf("wrong child_a sourcedir %q; want %q", got, want)
}
if got, want := len(childA.Module.ModuleCalls), 1; got != want {
t.Errorf("wrong number of module calls in child_a %d; want %d", got, want)
}
}