Skip to content

Commit

Permalink
Add package block generation to the terraform converter.
Browse files Browse the repository at this point in the history
This is the first step in supporting package blocks.  The next step is
supporting parameterization and detecting if the package should be
bridge with terraform-bridge or not.
  • Loading branch information
brandonpollack23 committed Dec 4, 2024
1 parent 8bb5ad2 commit 6cae4a6
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Improvements

- Add generation of pcl "package" blocks
- Add EOT (heredoc) style string delimiter handling.
- Add template join expression to convert expression
- Add references to issues for missing functions in output
Expand Down
20 changes: 20 additions & 0 deletions pkg/convert/testdata/programs/required_providers/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
terraform {
required_providers {
random = {
version = "~> 3.5.1"
source = "hashicorp/random"
}
null = {
source = "hashicorp/null"
}
tf-boundary = {
source = "hashicorp/boundary"
version = "1.1.9"
}
}
required_version = ">= 1.3.5"
}

output "asdfasdfasdf" {
value = 123
}
22 changes: 22 additions & 0 deletions pkg/convert/testdata/programs/required_providers/pcl/main.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package "random" {
baseProviderName = "random"
}

package "null" {
baseProviderName = "null"
}

package "tf-boundary" {
baseProviderName = "terraform-provider"
baseProviderVersion = "0.3.0"
parameterization "name" "version" "value" {
version = "1.1.9"
name = "tf-boundary"
value = "eyJyZW1vdGUiOnsidXJsIjoiaGFzaGljb3JwL2JvdW5kYXJ5IiwidmVyc2lvbiI6IjEuMS45In19"
}
}


output "asdfasdfasdf" {
value = 123
}
73 changes: 69 additions & 4 deletions pkg/convert/tf.go
Original file line number Diff line number Diff line change
Expand Up @@ -2668,6 +2668,7 @@ func translateRemoteModule(
destinationRoot afero.Fs, // The root of the destination filesystem to write PCL to.
destinationDirectory string, // A path in destination to write the translated code to.
info il.ProviderInfoSource,
requiredProviders map[string]*configs.RequiredProvider,
) hcl.Diagnostics {
fetcher := getmodules.NewPackageFetcher()
tempPath, err := os.MkdirTemp("", "pulumi-tf-registry")
Expand Down Expand Up @@ -2711,6 +2712,8 @@ func translateRemoteModule(
sourceRoot, "/",
destinationRoot, destinationDirectory,
info,
requiredProviders,
/*topLevelModule*/false,
)
}

Expand All @@ -2721,6 +2724,8 @@ func translateModuleSourceCode(
destinationRoot afero.Fs, // The root of the destination filesystem to write PCL to.
destinationDirectory string, // A path in destination to write the translated code to.
info il.ProviderInfoSource,
requiredProviders map[string]*configs.RequiredProvider,
topLevelModule bool,
) hcl.Diagnostics {
sources, module, moduleDiagnostics := loadConfigDir(sourceRoot, sourceDirectory)
if moduleDiagnostics.HasErrors() {
Expand All @@ -2729,6 +2734,10 @@ func translateModuleSourceCode(
return moduleDiagnostics
}

for name, provider := range module.ProviderRequirements.RequiredProviders {
requiredProviders[name] = provider
}

scopes := newScopes(info)

state := &convertState{
Expand Down Expand Up @@ -2926,7 +2935,10 @@ func translateModuleSourceCode(
sourcePath,
destinationRoot,
destinationPath,
info)
info,
requiredProviders,
/*topLevelModule*/false,
)
state.diagnostics = append(state.diagnostics, diags...)
if diags.HasErrors() {
return state.diagnostics
Expand Down Expand Up @@ -2957,7 +2969,8 @@ func translateModuleSourceCode(
addr.Subdir,
destinationRoot,
destinationPath,
info)
info,
requiredProviders)
state.diagnostics = append(state.diagnostics, diags...)
if diags.HasErrors() {
return state.diagnostics
Expand Down Expand Up @@ -3081,7 +3094,8 @@ func translateModuleSourceCode(
remoteAddr.Subdir,
destinationRoot,
destinationPath,
info)
info,
requiredProviders)
state.diagnostics = append(state.diagnostics, diags...)
if diags.HasErrors() {
return state.diagnostics
Expand Down Expand Up @@ -3236,6 +3250,22 @@ func translateModuleSourceCode(

body := file.Body()

// Add package metadata to only the top level module.
// This should be true for only the entry point into the conversion, all
// recursive module translations should set this false.
// This guarantees that the package blocks are only written in one place (eg main.pp)
if topLevelModule {
for name, provider := range requiredProviders {
block, d := getPackageBlock(name, provider)
state.diagnostics = append(state.diagnostics, d...)
body.AppendBlock(block)
body.AppendUnstructuredTokens(hclwrite.TokensForIdentifier("\n"))
}

// Only write the package block once
topLevelModule = false
}

// First handle any inputs, these will be picked up by the "vars" scope
if item.variable != nil {
leading, block, trailing := convertVariable(state, scopes, item.variable)
Expand Down Expand Up @@ -3356,12 +3386,47 @@ func translateModuleSourceCode(
return state.diagnostics
}


// getPackageBlock returns package block in the following form:
//
// package <name> {
// baseProviderName = <name>
// baseProviderVersion = <version>
// baseProviderDownloadUrl = <url>
// parameterization {
// name = <name>
// version = <version>
// value = <base64-encoded-value>
// }
// }
func getPackageBlock(name string, prov *configs.RequiredProvider) (*hclwrite.Block, hcl.Diagnostics) {
// if the name is one of our known providers just write it, if it is not it
// must be tf so write a paramaterized package.

block := hclwrite.NewBlock("package", []string{name})
body := block.Body()

diags := hcl.Diagnostics{}
body.SetAttributeValue("baseProviderName", cty.StringVal(name))

return block, diags
}


func TranslateModule(
source afero.Fs, sourceDirectory string,
destination afero.Fs, info il.ProviderInfoSource,
) hcl.Diagnostics {
modules := make(map[moduleKey]string)
return translateModuleSourceCode(modules, source, sourceDirectory, destination, "/", info)
return translateModuleSourceCode(modules,
source,
sourceDirectory,
destination,
"/",
info,
/*requiredProviders*/map[string]*configs.RequiredProvider{},
/*topLevelModule*/true,
)
}

func errorf(subject hcl.Range, f string, args ...interface{}) *hcl.Diagnostic {
Expand Down
5 changes: 5 additions & 0 deletions pkg/convert/translate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package convert

import (
"context"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -76,6 +77,10 @@ func (l *testLoader) LoadPackage(pkg string, version *semver.Version) (*schema.P
return schemaPackage, nil
}

func (l *testLoader) LoadPackageV2(ctx context.Context, descriptor *schema.PackageDescriptor) (*schema.Package, error) {
return l.LoadPackage(descriptor.Name, descriptor.Version)
}

func (l *testLoader) LoadPackageReference(pkg string, version *semver.Version) (schema.PackageReference, error) {
schemaPackage, err := l.LoadPackage(pkg, version)
if err != nil {
Expand Down

0 comments on commit 6cae4a6

Please sign in to comment.