Skip to content

Commit

Permalink
Merge pull request #19 from abuxton/abc-add-tfe_variable-format
Browse files Browse the repository at this point in the history
issue18 - add formats for API and TFE_resource
  • Loading branch information
shihanng authored Nov 26, 2021
2 parents cf751f3 + 064e5f2 commit 50fa461
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 4 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ variable "docker_ports" {
image_id = "abc"
```

- The `-r, --resource` flag outputs all variables as terraform resources for the `tfe_variable resource` found in the `tfe` provider <https://registry.terraform.io/providers/hashicorp/tfe/latest/docs/resources/variable>

- The `-w, --workspace` flag outputs all variables in the payload format for the API <https://www.terraform.io/docs/cloud/api/workspace-variables.html#sample-payload>. You can use `jq` to filter variables by key name.
```
$ tfvar -w . | jq '. | select(.data.attributes.key == "region")'
{
"data": {
...
}
}
```
For more info, checkout the `--help` page:
```
Expand All @@ -113,11 +125,13 @@ Flags:
-e, --env-var Print output in export TF_VAR_image_id=ami-abc123 format
-h, --help help for tfvar
--ignore-default Do not use defined default values
-r, --resource Print output in hashicorp/tfe tfe_variable resource format
--var stringArray Set a variable in the generated definitions.
This flag can be set multiple times.
--var-file stringArray Set variables from a file.
This flag can be set multiple times.
-v, --version version for tfvar
-w, --workspace Print output variables as payloads for workspace API
```
Expand Down
23 changes: 23 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ const (
flagDebug = "debug"
flagEnvVar = "env-var"
flagNoDefault = "ignore-default"
flagResource = "resource"
flagVar = "var"
flagVarFile = "var-file"
flagWorkspace = "workspace"
)

// New returns a new instance of cobra.Command for tfvar. Usage:
Expand Down Expand Up @@ -49,6 +51,8 @@ one would write it in variable definitions files (.tfvars).
variable definitions files e.g. terraform.tfvars[.json] *.auto.tfvars[.json]`)
rootCmd.PersistentFlags().BoolP(flagDebug, "d", false, "Print debug log on stderr")
rootCmd.PersistentFlags().BoolP(flagEnvVar, "e", false, "Print output in export TF_VAR_image_id=ami-abc123 format")
rootCmd.PersistentFlags().BoolP(flagResource, "r", false, "Print output in hashicorp/tfe tfe_variable resource format")
rootCmd.PersistentFlags().BoolP(flagWorkspace, "w", false, "Print output variables as payloads for workspace API")
rootCmd.PersistentFlags().Bool(flagNoDefault, false, "Do not use defined default values")
rootCmd.PersistentFlags().StringArray(flagVar, []string{}, `Set a variable in the generated definitions.
This flag can be set multiple times.`)
Expand Down Expand Up @@ -123,6 +127,16 @@ func (r *runner) rootRunE(cmd *cobra.Command, args []string) error {
return errors.Wrap(err, "cmd: get flag --auto-assign")
}

isWorkspace, err := cmd.PersistentFlags().GetBool(flagWorkspace)
if err != nil {
return errors.Wrap(err, "cmd: get flag --workspace")
}

isResource, err := cmd.PersistentFlags().GetBool(flagResource)
if err != nil {
return errors.Wrap(err, "cmd: get flag --resource")
}

unparseds := make(map[string]tfvar.UnparsedVariableValue)

if isAutoAssign {
Expand Down Expand Up @@ -172,5 +186,14 @@ func (r *runner) rootRunE(cmd *cobra.Command, args []string) error {
writer = tfvar.WriteAsEnvVars
}

if isWorkspace {
r.log.Debug("Print outputs in Workspace API payload format")
writer = tfvar.WriteAsWorkspacePayload
}

if isResource {
r.log.Debug("Print outputs in tfe_resource format")
writer = tfvar.WriteAsTFE_Resource
}
return writer(r.out, vars)
}
63 changes: 59 additions & 4 deletions pkg/tfvar/tfvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import (
// type = string
// }
type Variable struct {
Name string
Value cty.Value
Name string
Value cty.Value
Description string
Sensitive bool

parsingMode configs.VariableParsingMode
}
Expand All @@ -37,8 +39,10 @@ func Load(dir string) ([]Variable, error) {

for _, v := range modules.Variables {
variables = append(variables, Variable{
Name: v.Name,
Value: v.Default,
Name: v.Name,
Value: v.Default,
Description: v.Description,
Sensitive: v.Sensitive,

parsingMode: v.ParsingMode,
})
Expand Down Expand Up @@ -110,7 +114,58 @@ func WriteAsTFVars(w io.Writer, vars []Variable) error {
_, err := f.WriteTo(w)
return errors.Wrap(err, "tfvar: failed to write as tfvars")
}
func WriteAsWorkspacePayload(w io.Writer, vars []Variable) error {
var data error
for _, v := range vars {
val := convertNull(v.Value)

t := hclwrite.TokensForValue(val)
t = oneliner(t)
b := hclwrite.Format(t.Bytes())
b = bytes.TrimPrefix(b, []byte(`"`))
b = bytes.TrimSuffix(b, []byte(`"`))
b = bytes.ReplaceAll(b, []byte(`"`), []byte(`'`))
data := fmt.Sprintf(`{
"data": {
"type": "vars",
"attributes": {
"key": "%s",
"value": "%s",
"description": "%s",
"category": "%s",
"hcl": %s,
"sensitive": %v
}
}
}
`, v.Name, string(b), v.Description, "terraform", "false", v.Sensitive)
if _, err := fmt.Fprintf(w, "%s", data); err != nil {
return errors.Wrap(err, "tfvar: unexpected error writing payload")
}

}
return data
}
func WriteAsTFE_Resource(w io.Writer, vars []Variable) error {
f := hclwrite.NewEmptyFile()
rootBody := f.Body()

for _, v := range vars {
rootBody.AppendNewline()
resourceBlock := rootBody.AppendNewBlock("resource", []string{"tfe_variable", v.Name})
resourceBody := resourceBlock.Body()
resourceBody.SetAttributeValue("key", cty.StringVal(v.Name))
resourceBody.SetAttributeValue("value", v.Value)
resourceBody.SetAttributeValue("sensitive", cty.BoolVal(v.Sensitive))
resourceBody.SetAttributeValue("description", cty.StringVal(v.Description))
resourceBody.SetAttributeValue("workspace_id", cty.NilVal)
resourceBody.SetAttributeValue("category", cty.StringVal("terraform"))

}

_, err := f.WriteTo(w)
return errors.Wrap(err, "tfe_variable: failed to write as tfe_variable resource")
}
func convertNull(v cty.Value) cty.Value {
if v.IsNull() {
return cty.StringVal("")
Expand Down
165 changes: 165 additions & 0 deletions pkg/tfvar/tfvar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,168 @@ region = null
`
assert.Equal(t, expected, buf.String())
}
func TestWriteAsTFE_Resource(t *testing.T) {
vars, err := Load("testdata/defaults")
require.NoError(t, err)

sort.Slice(vars, func(i, j int) bool { return vars[i].Name < vars[j].Name })

var buf bytes.Buffer
assert.NoError(t, WriteAsTFE_Resource(&buf, vars))

expected := `
resource "tfe_variable" "availability_zone_names" {
key = "availability_zone_names"
value = ["us-west-1a"]
sensitive = false
description = ""
workspace_id = null
category = "terraform"
}
resource "tfe_variable" "aws_amis" {
key = "aws_amis"
value = {
eu-west-1 = "ami-b1cf19c6"
us-east-1 = "ami-de7ab6b6"
us-west-1 = "ami-3f75767a"
us-west-2 = "ami-21f78e11"
}
sensitive = false
description = ""
workspace_id = null
category = "terraform"
}
resource "tfe_variable" "docker_ports" {
key = "docker_ports"
value = [{
external = 8300
internal = 8301
protocol = "tcp"
}]
sensitive = false
description = ""
workspace_id = null
category = "terraform"
}
resource "tfe_variable" "instance_name" {
key = "instance_name"
value = "my-instance"
sensitive = false
description = ""
workspace_id = null
category = "terraform"
}
resource "tfe_variable" "password" {
key = "password"
value = null
sensitive = true
description = "the root password to use with the database"
workspace_id = null
category = "terraform"
}
resource "tfe_variable" "region" {
key = "region"
value = null
sensitive = false
description = ""
workspace_id = null
category = "terraform"
}
`
assert.Equal(t, expected, buf.String())
}
func TestWriteAsWorkspacePayload(t *testing.T) {
vars, err := Load("testdata/defaults")
require.NoError(t, err)

sort.Slice(vars, func(i, j int) bool { return vars[i].Name < vars[j].Name })

var buf bytes.Buffer
assert.NoError(t, WriteAsWorkspacePayload(&buf, vars))

expected := `{
"data": {
"type": "vars",
"attributes": {
"key": "availability_zone_names",
"value": "['us-west-1a']",
"description": "",
"category": "terraform",
"hcl": false,
"sensitive": false
}
}
}
{
"data": {
"type": "vars",
"attributes": {
"key": "aws_amis",
"value": "{ eu-west-1 = 'ami-b1cf19c6', us-east-1 = 'ami-de7ab6b6', us-west-1 = 'ami-3f75767a', us-west-2 = 'ami-21f78e11' }",
"description": "",
"category": "terraform",
"hcl": false,
"sensitive": false
}
}
}
{
"data": {
"type": "vars",
"attributes": {
"key": "docker_ports",
"value": "[{ external = 8300, internal = 8301, protocol = 'tcp' }]",
"description": "",
"category": "terraform",
"hcl": false,
"sensitive": false
}
}
}
{
"data": {
"type": "vars",
"attributes": {
"key": "instance_name",
"value": "my-instance",
"description": "",
"category": "terraform",
"hcl": false,
"sensitive": false
}
}
}
{
"data": {
"type": "vars",
"attributes": {
"key": "password",
"value": "",
"description": "the root password to use with the database",
"category": "terraform",
"hcl": false,
"sensitive": true
}
}
}
{
"data": {
"type": "vars",
"attributes": {
"key": "region",
"value": "",
"description": "",
"category": "terraform",
"hcl": false,
"sensitive": false
}
}
}
`
assert.Equal(t, expected, buf.String())
}

0 comments on commit 50fa461

Please sign in to comment.