Skip to content

Commit

Permalink
Add support for complex input variables [WIP]
Browse files Browse the repository at this point in the history
As requested in #7

This is incomplete, but it already works with primitive types and string
lists.

Needs to be extended to support complex list types.

Maps and objects may be added as well, but first lists need to be
implemented fully.

Also, the old state needs to be migrated to the new one (Variables type
changed) – or maybe let's introduce a new name and keep the old one for
compatibility.
  • Loading branch information
simaotwx committed Aug 12, 2024
1 parent e58d913 commit 55485c4
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 7 deletions.
20 changes: 20 additions & 0 deletions examples/example.pkr.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,26 @@ variable "test_var2" {
type = string
}

variable "test_int" {
type = number
}

variable "test_float" {
type = number
}

variable "test_big_float" {
type = number
}

variable "test_bool" {
type = bool
}

variable "test_list" {
type = list(string)
}

source "file" "example" {
content = ""
target = "/dev/null"
Expand Down
7 changes: 7 additions & 0 deletions examples/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ resource "packer_image" "image1" {
variables = {
test_var1 = "test 1"
test_var2 = "test 2"
test_int = 420
test_float = 3.1416
test_big_float = 1.234e100
test_bool = true
test_list = tolist([
"element 1", "element 2"
])
}

triggers = {
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ module terraform-provider-packer
go 1.22.6

require (
github.com/alecthomas/hcl v0.5.4
github.com/google/uuid v1.6.0
github.com/hashicorp/hcl/v2 v2.14.1
github.com/hashicorp/packer v1.10.0
github.com/hashicorp/terraform-plugin-framework v1.11.0
github.com/pkg/errors v0.9.1
Expand Down Expand Up @@ -42,6 +44,8 @@ require (
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/alecthomas/participle/v2 v2.0.0-beta.5 // indirect
github.com/alecthomas/repr v0.1.0 // indirect
github.com/apparentlymart/go-cidr v1.0.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-metrics v0.3.9 // indirect
Expand Down Expand Up @@ -118,7 +122,6 @@ require (
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/hcl/v2 v2.14.1 // indirect
github.com/hashicorp/hcp-sdk-go v0.36.0 // indirect
github.com/hashicorp/packer-plugin-amazon v1.2.1 // indirect
github.com/hashicorp/packer-plugin-ansible v1.0.3 // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/alecthomas/assert/v2 v2.0.3 h1:WKqJODfOiQG0nEJKFKzDIG3E29CN2/4zR9XGJzKIkbg=
github.com/alecthomas/assert/v2 v2.0.3/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA=
github.com/alecthomas/hcl v0.5.4 h1:4Z5dzFRpsXaphMVxJSPSdWP8Wqi2RJ+85WLdx3A0I0k=
github.com/alecthomas/hcl v0.5.4/go.mod h1:I7ywm4p2YDXBNdEItWJ5UhhRvO/IH2RAgRhlovB6kX0=
github.com/alecthomas/participle/v2 v2.0.0-beta.5 h1:y6dsSYVb1G5eK6mgmy+BgI3Mw35a3WghArZ/Hbebrjo=
github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0qdokritYSNR3wV5cVwmIEaMM=
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
Expand Down Expand Up @@ -504,6 +512,8 @@ github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/r
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
Expand Down
69 changes: 69 additions & 0 deletions hclconv/conv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package hclconv

import (
"fmt"
"reflect"
"strconv"
"strings"

"github.com/alecthomas/hcl"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/pkg/errors"
)

type ListProxy struct {
List []string
}

func ConvertDynamicAttributeToString(key string, elementValue attr.Value) (string, error) {
switch elementValue := elementValue.(type) {
case types.String:
return elementValue.ValueString(), nil
case types.Bool:
return strconv.FormatBool(elementValue.ValueBool()), nil
case types.Number:
return elementValue.ValueBigFloat().Text('e', 4), nil
case types.List:
result, err := MarshalTFListToHcl(elementValue.Elements())
if err != nil {
return "", errors.Wrap(err, "could not convert list to hcl")
}
return result, nil
case types.Set:
result, err := MarshalTFListToHcl(elementValue.Elements())
if err != nil {
return "", errors.Wrap(err, "could not convert set to hcl")
}
return result, nil
case types.Map:
return "", errors.New(
fmt.Sprintf("Maps are currently unsupported as variables (key %s)", key))
case types.Object:
return "", errors.New(
fmt.Sprintf("Objects are currently unsupported as variables (key %s)", key))
default:
return "", errors.New(
fmt.Sprintf("Unsupported type for variable %s in object: %s",
key,
reflect.TypeOf(elementValue).String()))
}
}

func MarshalTFListToHcl(elements []attr.Value) (string, error) {
convertedElements := make([]string, len(elements))
for i, element := range elements {
var err error
convertedElements[i], err = ConvertDynamicAttributeToString(strconv.FormatInt(int64(i), 10), element)
if err != nil {
return "", errors.Wrap(err, fmt.Sprintf("could not convert element %d to HCL", i))
}
}
byt, err := hcl.Marshal(&ListProxy{convertedElements})
if err != nil {
return "Failed to marshal elements to HCL", err
}
hclString := string(byt)
// Remove the "List = " prefix
return strings.TrimPrefix(hclString, "List = "), nil
}
41 changes: 35 additions & 6 deletions provider/resource_packer_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"fmt"
"os"
"os/exec"
"reflect"

"terraform-provider-packer/hclconv"
"terraform-provider-packer/packer_interop"

"github.com/hashicorp/terraform-plugin-framework/resource/schema"
Expand All @@ -25,7 +27,7 @@ import (

type resourceImageType struct {
ID types.String `tfsdk:"id"`
Variables map[string]string `tfsdk:"variables"`
Variables types.Dynamic `tfsdk:"variables"`
AdditionalParams []string `tfsdk:"additional_params"`
Directory types.String `tfsdk:"directory"`
File types.String `tfsdk:"file"`
Expand Down Expand Up @@ -64,9 +66,8 @@ func (r resourceImage) Schema(_ context.Context, _ resource.SchemaRequest, respo
Description: "Name of this build. This value is not passed to Packer.",
Optional: true,
},
"variables": schema.MapAttribute{
"variables": schema.DynamicAttribute{
Description: "Variables to pass to Packer",
ElementType: types.StringType,
Optional: true,
},
"additional_params": schema.SetAttribute{
Expand Down Expand Up @@ -140,7 +141,7 @@ func RunCommandInDirWithEnvReturnOutput(
for key, value := range env {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value))
}
output, err := cmd.Output()
output, err := cmd.CombinedOutput()
if err != nil {
// Create a JSON of the parameters to make it crystal clear
// what was passed to the command.
Expand Down Expand Up @@ -181,8 +182,36 @@ func (r resourceImage) packerBuild(resourceState *resourceImageType, diags *diag
envVars := packer_interop.EnvVars(resourceState.Environment, !resourceState.IgnoreEnvironment.ValueBool())

params := []string{"build"}
for key, value := range resourceState.Variables {
params = append(params, "-var", key+"="+value)
if !resourceState.Variables.IsNull() && !resourceState.Variables.IsUnknown() &&
!resourceState.Variables.IsUnderlyingValueNull() && !resourceState.Variables.IsUnderlyingValueUnknown() {
switch value := resourceState.Variables.UnderlyingValue().(type) {
case types.Map:
for key, elementValue := range value.Elements() {
finalValue, err := hclconv.ConvertDynamicAttributeToString(key, elementValue)
if err != nil {
return errors.Wrap(err, fmt.Sprintf(
"could not convert dynamic value (%s, type %s) to string",
key,
reflect.TypeOf(elementValue).String()))
}
params = append(params, "-var", key+"="+finalValue)
}
case types.Object:
for key, elementValue := range value.Attributes() {
finalValue, err := hclconv.ConvertDynamicAttributeToString(key, elementValue)
if err != nil {
return errors.Wrap(err, fmt.Sprintf(
"could not convert dynamic value (%s, type %s) to string",
key,
reflect.TypeOf(elementValue).String()))
}
params = append(params, "-var", key+"="+finalValue)
}
default:
return errors.New(
"only maps and objects are supported for the variables attribute. Instead got: " +
reflect.TypeOf(resourceState.Variables.UnderlyingValue()).String())
}
}
if resourceState.Force.ValueBool() {
params = append(params, "-force")
Expand Down

0 comments on commit 55485c4

Please sign in to comment.