Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: load variables from tfvars files#11549

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
mtojek merged 19 commits intomainfrom8501-parse-tfvars-3
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletionscli/templatecreate.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -107,6 +107,18 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {

message := uploadFlags.templateMessage(inv)

var varsFiles []string
if !uploadFlags.stdin() {
varsFiles, err = DiscoverVarsFiles(uploadFlags.directory)
if err != nil {
return err
}
Comment on lines +112 to +115
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I could imagine a case where someone had tfvars files lying around unused before, worked around the issue, and left them there. Now we're going to auto-discover them. I'm not sure what this will break.
Should at the very least add an info message about the auto-discovered vars files?

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Ok, I changed the code to print a message alerting about the presence of tfvars 👍


if len(varsFiles) > 0 {
_, _ = fmt.Fprintln(inv.Stdout, "Auto-discovered Terraform tfvars files. Make sure to review and clean up any unused files.")
}
}

// Confirm upload of the directory.
resp, err := uploadFlags.upload(inv, client)
if err != nil {
Expand All@@ -119,6 +131,7 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {
}

userVariableValues, err := ParseUserVariableValues(
varsFiles,
variablesFile,
commandLineVariables)
if err != nil {
Expand Down
13 changes: 13 additions & 0 deletionscli/templatepush.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -78,6 +78,18 @@ func (r *RootCmd) templatePush() *clibase.Cmd {

message := uploadFlags.templateMessage(inv)

var varsFiles []string
if !uploadFlags.stdin() {
varsFiles, err = DiscoverVarsFiles(uploadFlags.directory)
if err != nil {
return err
}

if len(varsFiles) > 0 {
_, _ = fmt.Fprintln(inv.Stdout, "Auto-discovered Terraform tfvars files. Make sure to review and clean up any unused files.")
}
}

resp, err := uploadFlags.upload(inv, client)
if err != nil {
return err
Expand All@@ -89,6 +101,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd {
}

userVariableValues, err := ParseUserVariableValues(
varsFiles,
variablesFile,
commandLineVariables)
if err != nil {
Expand Down
180 changes: 178 additions & 2 deletionscli/templatevariables.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,65 @@
package cli

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"sort"
"strings"

"golang.org/x/xerrors"
"gopkg.in/yaml.v3"

"github.com/hashicorp/hcl/v2/hclparse"
"github.com/zclconf/go-cty/cty"

"github.com/coder/coder/v2/codersdk"
)

func ParseUserVariableValues(variablesFile string, commandLineVariables []string) ([]codersdk.VariableValue, error) {
/**
* DiscoverVarsFiles function loads vars files in a predefined order:
* 1. terraform.tfvars
* 2. terraform.tfvars.json
* 3. *.auto.tfvars
* 4. *.auto.tfvars.json
*/
func DiscoverVarsFiles(workDir string) ([]string, error) {
var found []string

fi, err := os.Stat(filepath.Join(workDir, "terraform.tfvars"))
if err == nil {
found = append(found, filepath.Join(workDir, fi.Name()))
} else if !os.IsNotExist(err) {
return nil, err
}

fi, err = os.Stat(filepath.Join(workDir, "terraform.tfvars.json"))
if err == nil {
found = append(found, filepath.Join(workDir, fi.Name()))
} else if !os.IsNotExist(err) {
return nil, err
}

dirEntries, err := os.ReadDir(workDir)
if err != nil {
return nil, err
}

for _, dirEntry := range dirEntries {
if strings.HasSuffix(dirEntry.Name(), ".auto.tfvars") || strings.HasSuffix(dirEntry.Name(), ".auto.tfvars.json") {
found = append(found, filepath.Join(workDir, dirEntry.Name()))
}
}
return found, nil
}

func ParseUserVariableValues(varsFiles []string, variablesFile string, commandLineVariables []string) ([]codersdk.VariableValue, error) {
fromVars, err := parseVariableValuesFromVarsFiles(varsFiles)
if err != nil {
return nil, err
}

fromFile, err := parseVariableValuesFromFile(variablesFile)
if err != nil {
return nil, err
Expand All@@ -21,7 +70,131 @@ func ParseUserVariableValues(variablesFile string, commandLineVariables []string
return nil, err
}

return combineVariableValues(fromFile, fromCommandLine), nil
return combineVariableValues(fromVars, fromFile, fromCommandLine), nil
}

func parseVariableValuesFromVarsFiles(varsFiles []string) ([]codersdk.VariableValue, error) {
var parsed []codersdk.VariableValue
for _, varsFile := range varsFiles {
content, err := os.ReadFile(varsFile)
if err != nil {
return nil, err
}

var t []codersdk.VariableValue
ext := filepath.Ext(varsFile)
switch ext {
case ".tfvars":
t, err = parseVariableValuesFromHCL(content)
if err != nil {
return nil, xerrors.Errorf("unable to parse HCL content: %w", err)
}
case ".json":
t, err = parseVariableValuesFromJSON(content)
if err != nil {
return nil, xerrors.Errorf("unable to parse JSON content: %w", err)
}
default:
return nil, xerrors.Errorf("unexpected tfvars format: %s", ext)
}

parsed = append(parsed, t...)
}
return parsed, nil
}

func parseVariableValuesFromHCL(content []byte) ([]codersdk.VariableValue, error) {
parser := hclparse.NewParser()
hclFile, diags := parser.ParseHCL(content, "file.hcl")
if diags.HasErrors() {
return nil, diags
}

attrs, diags := hclFile.Body.JustAttributes()
if diags.HasErrors() {
return nil, diags
}

stringData := map[string]string{}
for _, attribute := range attrs {
ctyValue, diags := attribute.Expr.Value(nil)
if diags.HasErrors() {
return nil, diags
}

ctyType := ctyValue.Type()
if ctyType.Equals(cty.String) {
stringData[attribute.Name] = ctyValue.AsString()
} else if ctyType.Equals(cty.Number) {
stringData[attribute.Name] = ctyValue.AsBigFloat().String()
} else if ctyType.IsTupleType() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I immediately saw this, thought 🤔 "y no switch", and then saw how annoying this is.

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yes, unfortunately the API isn't too friendly :)

// In case of tuples, Coder only supports the list(string) type.
var items []string
var err error
_ = ctyValue.ForEachElement(func(key, val cty.Value) (stop bool) {
if !val.Type().Equals(cty.String) {
err = xerrors.Errorf("unsupported tuple item type: %s ", val.GoString())
return true
}
items = append(items, val.AsString())
return false
})
if err != nil {
return nil, err
}

m, err := json.Marshal(items)
if err != nil {
return nil, err
}
stringData[attribute.Name] = string(m)
} else {
return nil, xerrors.Errorf("unsupported value type (name: %s): %s", attribute.Name, ctyType.GoString())
}
}

return convertMapIntoVariableValues(stringData), nil
}

// parseVariableValuesFromJSON converts the .tfvars.json content into template variables.
// The function visits only root-level properties as template variables do not support nested
// structures.
func parseVariableValuesFromJSON(content []byte) ([]codersdk.VariableValue, error) {
var data map[string]interface{}
err := json.Unmarshal(content, &data)
if err != nil {
return nil, err
}

stringData := map[string]string{}
for key, value := range data {
switch value.(type) {
case string, int, bool:
stringData[key] = fmt.Sprintf("%v", value)
default:
m, err := json.Marshal(value)
if err != nil {
return nil, err
}
stringData[key] = string(m)
}
}

return convertMapIntoVariableValues(stringData), nil
}

func convertMapIntoVariableValues(m map[string]string) []codersdk.VariableValue {
var parsed []codersdk.VariableValue
for key, value := range m {
parsed = append(parsed, codersdk.VariableValue{
Name: key,
Value: value,
})
}
sort.Slice(parsed, func(i, j int) bool {
return parsed[i].Name < parsed[j].Name
})
return parsed
}

func parseVariableValuesFromFile(variablesFile string) ([]codersdk.VariableValue, error) {
Expand DownExpand Up@@ -94,5 +267,8 @@ func combineVariableValues(valuesSets ...[]codersdk.VariableValue) []codersdk.Va
result = append(result, codersdk.VariableValue{Name: name, Value: value})
}

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

[8]ページ先頭

©2009-2025 Movatter.jp