Skip to content

Commit

Permalink
Support generating provider config
Browse files Browse the repository at this point in the history
  • Loading branch information
magodo committed Mar 3, 2024
1 parent 3a6c13b commit 092f2d1
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 1 deletion.
21 changes: 21 additions & 0 deletions tfadd/internal/state_to_tpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ import (
"github.com/zclconf/go-cty/cty"
)

func ProviderTpl(name string, v cty.Value, schema *tfjson.SchemaBlock) ([]byte, error) {
var buf strings.Builder
buf.WriteString(fmt.Sprintf("provider %q {\n", name))
if err := addAttributes(&buf, v, schema.Attributes, 2); err != nil {
return nil, err
}
if err := addBlocks(&buf, v, schema.NestedBlocks, 2); err != nil {
return nil, err
}
buf.WriteString("}\n")
return hclwrite.Format([]byte(buf.String())), nil
}

func StateToTpl(r *tfstate.StateResource, schema *tfjson.SchemaBlock) ([]byte, error) {
var buf strings.Builder
addr, err := addr2.ParseResourceAddr(r.Address)
Expand Down Expand Up @@ -217,6 +230,14 @@ func addNestedBlock(buf *strings.Builder, name string, schema *tfjson.SchemaBloc
if stateVal.IsNull() {
return nil
}

// // Converting the List and Set modes that have single-element constraint to Single mode.
// // This is how the legacy SDK defines the Single mode.
// if schema.MaxItems == 1 &&
// slices.Index([]tfjson.SchemaNestingMode{tfjson.SchemaNestingModeList, tfjson.SchemaNestingModeSet}, schema.NestingMode) != -1 {
// schema.NestingMode = tfjson.SchemaNestingModeSingle
// }

switch schema.NestingMode {
case tfjson.SchemaNestingModeSingle, tfjson.SchemaNestingModeGroup:
buf.WriteString(strings.Repeat(" ", indent))
Expand Down
5 changes: 5 additions & 0 deletions tfadd/tfadd_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/magodo/tfadd/tfadd/internal"
"github.com/zclconf/go-cty/cty"

"github.com/magodo/tfadd/addr"

Expand Down Expand Up @@ -196,3 +197,7 @@ func GenerateForOneResource(rsch *tfjson.Schema, res tfstate.StateResource, full
}
return b, nil
}

func GenerateForProvider(name string, psch *tfjson.Schema, v cty.Value) ([]byte, error) {
return internal.ProviderTpl(name, v, psch.Block)
}
83 changes: 82 additions & 1 deletion tfadd/tfadd_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package tfadd
import (
"bytes"
"context"
"encoding/json"
"os"
"path/filepath"
"regexp"
"strings"
"testing"

install "github.com/hashicorp/hc-install"
Expand All @@ -14,7 +16,9 @@ import (
"github.com/hashicorp/hc-install/product"
"github.com/hashicorp/hc-install/src"
"github.com/hashicorp/terraform-exec/tfexec"
"github.com/magodo/tfstate/terraform/jsonschema"
"github.com/stretchr/testify/require"
ctyjson "github.com/zclconf/go-cty/cty/json"
)

const (
Expand All @@ -26,7 +30,7 @@ const (
ENV_TFADD_DEV_PROVIDER = "TFADD_DEV_PROVIDER"
)

func TestTFAdd_state(t *testing.T) {
func TestTFAdd_resource(t *testing.T) {
if os.Getenv(ENV_TFADD_E2E) == "" {
t.Skipf("Skipping e2e test as %q is not set", ENV_TFADD_E2E)
}
Expand Down Expand Up @@ -190,3 +194,80 @@ resource "null_resource" "test" {
})
}
}

func TestTFAdd_provider(t *testing.T) {
if os.Getenv(ENV_TFADD_E2E) == "" {
t.Skipf("Skipping e2e test as %q is not set", ENV_TFADD_E2E)
}

// Ensure terraform executable
ctx := context.TODO()
i := install.NewInstaller()
tfexecutable, err := i.Ensure(ctx, []src.Source{
&tffs.AnyVersion{
Product: &product.Terraform,
},
&checkpoint.LatestVersion{
Product: product.Terraform,
},
})
if err != nil {
t.Fatalf("failed to install terraform: %v", err)
}

cases := []struct {
name string
providerFQDN string
v map[string]interface{}
expectError *regexp.Regexp
expect string
}{
{
name: "generate provider config",
providerFQDN: "registry.terraform.io/hashicorp/azurerm",
v: map[string]interface{}{
"features": []interface{}{map[string]interface{}{}},
"client_id": "client1",
},
expect: `provider "azurerm" {
client_id = "client1"
features {
}
}
`,
},
}

for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
wsp := t.TempDir()
tf, err := tfexec.NewTerraform(wsp, tfexecutable)
require.NoError(t, err)
ctx := context.Background()
require.NoError(t, os.WriteFile(filepath.Join(wsp, "terraform.tf"), []byte(`provider "azurerm" {
features {}
}`), 0644))
require.NoError(t, tf.Init(ctx))

pschs, err := tf.ProvidersSchema(context.TODO())
require.NoError(t, err)
psch := pschs.Schemas[tt.providerFQDN].ConfigSchema

b, err := json.Marshal(tt.v)
require.NoError(t, err)

schema := jsonschema.SchemaBlockImpliedType(psch.Block)
ctyval, err := ctyjson.Unmarshal(b, schema)

providerName := strings.Split(tt.providerFQDN, "/")[2]
b, err = GenerateForProvider(providerName, psch, ctyval)
if tt.expectError != nil {
require.Regexp(t, tt.expectError, err.Error())
return
}
require.NoError(t, err)
require.Equal(t, tt.expect, string(b))
return
})
}
}

0 comments on commit 092f2d1

Please sign in to comment.