From b1c22485d152462c68b88bf0dcb41713fe829f28 Mon Sep 17 00:00:00 2001 From: Fraser Waters Date: Mon, 14 Nov 2022 15:26:30 +0000 Subject: [PATCH] Test invoke using new mapping interfaces --- go.mod | 4 ++ go.sum | 4 -- pkg/tf2pulumi/convert/eject.go | 10 +++-- pkg/tf2pulumi/convert/eject_test.go | 23 ++++++++++- .../convert/testdata/mappings/simpledata.json | 38 +++++++++++++++++++ .../convert/testdata/schemas/simpledata.json | 27 +++++++++++++ .../convert/testdata/simple_invoke/main.tf | 8 ++++ .../testdata/simple_invoke/pcl/main.pp | 7 ++++ pkg/tf2pulumi/il/plugin_info.go | 32 ++++++++++++++++ pkg/tfbridge/info.go | 2 +- 10 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 pkg/tf2pulumi/convert/testdata/mappings/simpledata.json create mode 100644 pkg/tf2pulumi/convert/testdata/schemas/simpledata.json create mode 100644 pkg/tf2pulumi/convert/testdata/simple_invoke/main.tf create mode 100644 pkg/tf2pulumi/convert/testdata/simple_invoke/pcl/main.pp diff --git a/go.mod b/go.mod index feb8e9f89..9e3f53d3c 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,10 @@ module github.com/pulumi/pulumi-terraform-bridge/v3 go 1.18 +replace github.com/pulumi/pulumi/pkg/v3 => ../pulumi/pkg + +replace github.com/pulumi/pulumi/sdk/v3 => ../pulumi/sdk + require ( github.com/apparentlymart/go-cidr v1.1.0 github.com/blang/semver v3.5.1+incompatible diff --git a/go.sum b/go.sum index b41ec80dc..0e478850e 100644 --- a/go.sum +++ b/go.sum @@ -1548,10 +1548,6 @@ github.com/pulumi/pulumi-java/pkg v0.6.0 h1:haiSQJlhrQIBBcR0r0aQCIF8i69e4znzRnHp github.com/pulumi/pulumi-java/pkg v0.6.0/go.mod h1:xSK2B792P8zjwYZTHYapMM1RJdue2BpRFQNYObWO0C8= github.com/pulumi/pulumi-yaml v1.0.2 h1:8fuoFNJlYJm1ni1Fff8QsIvef3E+ilroTPDrYKyy088= github.com/pulumi/pulumi-yaml v1.0.2/go.mod h1:FKly+y0x5onXHEZALNnFglr6ZZnro4Y/jlN4sYLKYeM= -github.com/pulumi/pulumi/pkg/v3 v3.46.1 h1:YwEAckUmXUM/ELWiPRk9/ZuPv9bogJgDLh11x6VdGtI= -github.com/pulumi/pulumi/pkg/v3 v3.46.1/go.mod h1:Rebr+SHHGtTu2w7VURQZDEUYDhtvjECoovIfh9TYHQU= -github.com/pulumi/pulumi/sdk/v3 v3.46.1 h1:z9I0dLP2FPGTrNSIuUtShTNe1SZLSK0//tmWrjDfFrI= -github.com/pulumi/pulumi/sdk/v3 v3.46.1/go.mod h1:n5EPRVFDh+EFwYZ+oaZmEtwgVN1A6NnI82B7ks/bmTU= github.com/pulumi/schema-tools v0.1.0 h1:o1nVQaJEnmcA+3trxe+0sIDlrILxEIOvgUQ2Ze4OFsk= github.com/pulumi/schema-tools v0.1.0/go.mod h1:feL1siLWdcCNUm+irXoHyNHbGaqoX7pfYojpGZe2ziY= github.com/pulumi/terraform-diff-reader v0.0.0-20201211191010-ad4715e9285e h1:Dik4Qe/+xguB8JagPyXNlbOnRiXGmq/PSPQTGunYnTk= diff --git a/pkg/tf2pulumi/convert/eject.go b/pkg/tf2pulumi/convert/eject.go index 1a706ca35..71a42cff3 100644 --- a/pkg/tf2pulumi/convert/eject.go +++ b/pkg/tf2pulumi/convert/eject.go @@ -25,6 +25,7 @@ import ( "github.com/spf13/afero" "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tf2pulumi/il" + "github.com/pulumi/pulumi/pkg/v3/codegen/convert" "github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/syntax" "github.com/pulumi/pulumi/pkg/v3/codegen/pcl" "github.com/pulumi/pulumi/pkg/v3/codegen/schema" @@ -82,14 +83,15 @@ func (o EjectOptions) logf(format string, arguments ...interface{}) { } // Eject converts a Terraform module at the provided location into a Pulumi module. -func Eject(dir string, loader schema.ReferenceLoader) (*workspace.Project, *pcl.Program, error) { +func Eject(dir string, loader schema.ReferenceLoader, mapper convert.Mapper) (*workspace.Project, *pcl.Program, error) { if loader == nil { panic("must provide a non-nil loader") } opts := EjectOptions{ - Root: afero.NewBasePathFs(afero.NewOsFs(), dir), - Loader: loader, + Root: afero.NewBasePathFs(afero.NewOsFs(), dir), + Loader: loader, + ProviderInfoSource: il.NewMapperProviderInfoSource(mapper), } tfFiles, program, diags, err := internalEject(opts) @@ -102,7 +104,7 @@ func Eject(dir string, loader schema.ReferenceLoader) (*workspace.Project, *pcl. return nil, nil, err } } - if err != nil { + if err != nil || diags.HasErrors() { return nil, nil, fmt.Errorf("failed to load Terraform configuration, %v", err) } diff --git a/pkg/tf2pulumi/convert/eject_test.go b/pkg/tf2pulumi/convert/eject_test.go index 12eac000d..f348d46db 100644 --- a/pkg/tf2pulumi/convert/eject_test.go +++ b/pkg/tf2pulumi/convert/eject_test.go @@ -75,6 +75,24 @@ func (l *testLoader) LoadPackageReference(pkg string, version *semver.Version) ( return schemaPackage.Reference(), nil } +type testMapper struct { + path string +} + +func (l *testMapper) GetMapping(provider string) ([]byte, error) { + mappingPath := filepath.Join(l.path, provider) + ".json" + + mappingBytes, err := os.ReadFile(mappingPath) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + + return mappingBytes, nil +} + func TestEject(t *testing.T) { // Test framework for eject // Each folder in testdata has a pcl folder, we check that if we convert the hcl we get the expected pcl @@ -89,7 +107,7 @@ func TestEject(t *testing.T) { }, 0) for _, info := range infos { // Skip the "schemas" directory, that's for test schemas not for tests themselves - if info.IsDir() && info.Name() != "schemas" { + if info.IsDir() && info.Name() != "schemas" && info.Name() != "mappings" { tests = append(tests, struct { name string path string @@ -101,6 +119,7 @@ func TestEject(t *testing.T) { } loader := &testLoader{path: filepath.Join(testDir, "schemas")} + mapper := &testMapper{path: filepath.Join(testDir, "mappings")} for _, tt := range tests { tt := tt // avoid capturing loop variable in the closure @@ -111,7 +130,7 @@ func TestEject(t *testing.T) { hclPath := tt.path pclPath := filepath.Join(tt.path, "pcl") - project, program, err := Eject(hclPath, loader) + project, program, err := Eject(hclPath, loader, mapper) if !assert.NoError(t, err) { return } diff --git a/pkg/tf2pulumi/convert/testdata/mappings/simpledata.json b/pkg/tf2pulumi/convert/testdata/mappings/simpledata.json new file mode 100644 index 000000000..9a2f27dc1 --- /dev/null +++ b/pkg/tf2pulumi/convert/testdata/mappings/simpledata.json @@ -0,0 +1,38 @@ +{ + "name": "simpledata", + "provider": { + "dataSources": { + "simpledata_source": { + "input_one": { + "type": 4 + }, + "input_two": { + "type": 2 + }, + "result": { + "type": 4, + "computed": true + } + } + } + }, + "dataSources": { + "simpledata_source": { + "tok": "simpledata::source", + "fields": { + "input_one": { + "name": "input_one", + "type": "string" + }, + "input_two": { + "name": "input_two", + "type": "integer" + }, + "result": { + "name": "result", + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/pkg/tf2pulumi/convert/testdata/schemas/simpledata.json b/pkg/tf2pulumi/convert/testdata/schemas/simpledata.json new file mode 100644 index 000000000..01d11ac11 --- /dev/null +++ b/pkg/tf2pulumi/convert/testdata/schemas/simpledata.json @@ -0,0 +1,27 @@ +{ + "version": "0.0.1", + "name": "simpledata", + "functions":{ + "simpledata::source": { + "inputs": { + "type": "object", + "properties": { + "input_one": { + "type": "string" + }, + "input_two": { + "type": "integer" + } + } + }, + "outputs": { + "properties": { + "result": { + "type": "string" + } + }, + "type": "object" + } + } + } +} \ No newline at end of file diff --git a/pkg/tf2pulumi/convert/testdata/simple_invoke/main.tf b/pkg/tf2pulumi/convert/testdata/simple_invoke/main.tf new file mode 100644 index 000000000..e96c32aa1 --- /dev/null +++ b/pkg/tf2pulumi/convert/testdata/simple_invoke/main.tf @@ -0,0 +1,8 @@ +data "simpledata_source" "a_data_source" { + input_one = "hello" + input_two = true +} + +output "some_output" { + value = data.simpledata_source.a_data_source.result +} \ No newline at end of file diff --git a/pkg/tf2pulumi/convert/testdata/simple_invoke/pcl/main.pp b/pkg/tf2pulumi/convert/testdata/simple_invoke/pcl/main.pp new file mode 100644 index 000000000..d2eeb38b7 --- /dev/null +++ b/pkg/tf2pulumi/convert/testdata/simple_invoke/pcl/main.pp @@ -0,0 +1,7 @@ +aDataSource = invoke("simpledata::source", { + input_one = "hello", + input_two = true +}) +output someOutput { + value = aDataSource.result +} \ No newline at end of file diff --git a/pkg/tf2pulumi/il/plugin_info.go b/pkg/tf2pulumi/il/plugin_info.go index ad64c784a..749545ae2 100644 --- a/pkg/tf2pulumi/il/plugin_info.go +++ b/pkg/tf2pulumi/il/plugin_info.go @@ -15,6 +15,7 @@ package il import ( + "encoding/json" "fmt" "net/http" "net/url" @@ -23,6 +24,7 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" + "github.com/pulumi/pulumi/pkg/v3/codegen/convert" "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" @@ -36,6 +38,36 @@ type ProviderInfoSource interface { GetProviderInfo(registry, namespace, name, version string) (*tfbridge.ProviderInfo, error) } +// mapperProviderInfoSource wraps a convert.Mapper to return tfbridge.ProviderInfo +type mapperProviderInfoSource struct { + mapper convert.Mapper +} + +func NewMapperProviderInfoSource(mapper convert.Mapper) ProviderInfoSource { + return &mapperProviderInfoSource{mapper: mapper} +} + +func (mapper *mapperProviderInfoSource) GetProviderInfo( + registryName, namespace, name, version string) (*tfbridge.ProviderInfo, error) { + + data, err := mapper.mapper.GetMapping(name) + if err != nil { + return nil, err + } + if data == nil { + message := fmt.Sprintf("could not find mapping information for provider %s", name) + message += "; try installing a pulumi plugin that supports this terraform provider" + return nil, fmt.Errorf(message) + } + + var info *tfbridge.MarshallableProviderInfo + err = json.Unmarshal(data, &info) + if err != nil { + return nil, fmt.Errorf("could not decode schema information for provider %s: %w", name, err) + } + return info.Unmarshal(), nil +} + // CachingProviderInfoSource wraps a ProviderInfoSource in a cache for faster access. type CachingProviderInfoSource struct { m sync.RWMutex diff --git a/pkg/tfbridge/info.go b/pkg/tfbridge/info.go index 4b7681571..274285b61 100644 --- a/pkg/tfbridge/info.go +++ b/pkg/tfbridge/info.go @@ -397,7 +397,7 @@ type PreConfigureCallbackWithLogger func( ) error // The types below are marshallable versions of the schema descriptions associated with a provider. These are used when -// marshalling a provider info as JSON; Note that these types only represent a subset of the informatino associated +// marshalling a provider info as JSON; Note that these types only represent a subset of the information associated // with a ProviderInfo; thus, a ProviderInfo cannot be round-tripped through JSON. // MarshallableSchema is the JSON-marshallable form of a Terraform schema.