github.com/jpreese/tflint@v0.19.2-0.20200908152133-b01686250fb6/tflint/terraform.go (about)

     1  package tflint
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	hcl "github.com/hashicorp/hcl/v2"
    13  	"github.com/hashicorp/hcl/v2/hclsyntax"
    14  	"github.com/hashicorp/terraform/configs"
    15  	"github.com/hashicorp/terraform/terraform"
    16  	"github.com/zclconf/go-cty/cty"
    17  )
    18  
    19  var defaultValuesFile = "terraform.tfvars"
    20  
    21  // ParseTFVariables parses the passed Terraform variable CLI arguments, and returns terraform.InputValues
    22  func ParseTFVariables(vars []string, declVars map[string]*configs.Variable) (terraform.InputValues, error) {
    23  	variables := make(terraform.InputValues)
    24  	for _, raw := range vars {
    25  		idx := strings.Index(raw, "=")
    26  		if idx == -1 {
    27  			return variables, fmt.Errorf("`%s` is invalid. Variables must be `key=value` format", raw)
    28  		}
    29  		name := raw[:idx]
    30  		rawVal := raw[idx+1:]
    31  
    32  		var mode configs.VariableParsingMode
    33  		declVar, declared := declVars[name]
    34  		if declared {
    35  			mode = declVar.ParsingMode
    36  		} else {
    37  			mode = configs.VariableParseLiteral
    38  		}
    39  
    40  		val, diags := mode.Parse(name, rawVal)
    41  		if diags.HasErrors() {
    42  			return variables, diags
    43  		}
    44  
    45  		variables[name] = &terraform.InputValue{
    46  			Value:      val,
    47  			SourceType: terraform.ValueFromCLIArg,
    48  		}
    49  	}
    50  
    51  	return variables, nil
    52  }
    53  
    54  // ParseExpression is a wrapper for a function that parses JSON and HCL expressions
    55  func ParseExpression(src []byte, filename string, start hcl.Pos) (hcl.Expression, hcl.Diagnostics) {
    56  	if strings.HasSuffix(filename, ".tf") {
    57  		return hclsyntax.ParseExpression(src, filename, start)
    58  	}
    59  
    60  	if strings.HasSuffix(filename, ".tf.json") {
    61  		return nil, hcl.Diagnostics{
    62  			&hcl.Diagnostic{
    63  				Severity: hcl.DiagError,
    64  				Summary:  "JSON configuration syntax is not supported",
    65  				Subject:  &hcl.Range{Filename: filename, Start: start, End: start},
    66  			},
    67  		}
    68  	}
    69  
    70  	panic(fmt.Sprintf("Unexpected file: %s", filename))
    71  }
    72  
    73  // HCLBodyRange attempts to find a range of the passed body
    74  func HCLBodyRange(body hcl.Body, defRange hcl.Range) hcl.Range {
    75  	if strings.HasSuffix(defRange.Filename, ".tf") {
    76  		var bodyRange hcl.Range
    77  
    78  		// Estimate the range of the body from the range of all attributes and blocks.
    79  		hclBody := body.(*hclsyntax.Body)
    80  		for _, attr := range hclBody.Attributes {
    81  			if bodyRange.Empty() {
    82  				bodyRange = attr.Range()
    83  			} else {
    84  				bodyRange = hcl.RangeOver(bodyRange, attr.Range())
    85  			}
    86  		}
    87  		for _, block := range hclBody.Blocks {
    88  			if bodyRange.Empty() {
    89  				bodyRange = block.Range()
    90  			} else {
    91  				bodyRange = hcl.RangeOver(bodyRange, block.Range())
    92  			}
    93  		}
    94  		return bodyRange
    95  	}
    96  
    97  	if strings.HasSuffix(defRange.Filename, ".tf.json") {
    98  		// HACK: In JSON syntax, DefRange corresponds to open brace and MissingItemRange corresponds to close brace.
    99  		return hcl.RangeOver(defRange, body.MissingItemRange())
   100  	}
   101  
   102  	panic(fmt.Sprintf("Unexpected file: %s", defRange.Filename))
   103  }
   104  
   105  func getTFDataDir() string {
   106  	dir := os.Getenv("TF_DATA_DIR")
   107  	if dir != "" {
   108  		log.Printf("[INFO] TF_DATA_DIR environment variable found: %s", dir)
   109  	} else {
   110  		dir = ".terraform"
   111  	}
   112  
   113  	return dir
   114  }
   115  
   116  func getTFModuleDir() string {
   117  	return filepath.Join(getTFDataDir(), "modules")
   118  }
   119  
   120  func getTFModuleManifestPath() string {
   121  	return filepath.Join(getTFModuleDir(), "modules.json")
   122  }
   123  
   124  func getTFWorkspace() string {
   125  	if envVar := os.Getenv("TF_WORKSPACE"); envVar != "" {
   126  		log.Printf("[INFO] TF_WORKSPACE environment variable found: %s", envVar)
   127  		return envVar
   128  	}
   129  
   130  	envData, _ := ioutil.ReadFile(filepath.Join(getTFDataDir(), "environment"))
   131  	current := string(bytes.TrimSpace(envData))
   132  	if current != "" {
   133  		log.Printf("[INFO] environment file found: %s", current)
   134  	} else {
   135  		current = "default"
   136  	}
   137  
   138  	return current
   139  }
   140  
   141  func getTFEnvVariables() terraform.InputValues {
   142  	envVariables := make(terraform.InputValues)
   143  	for _, e := range os.Environ() {
   144  		idx := strings.Index(e, "=")
   145  		envKey := e[:idx]
   146  		envVal := e[idx+1:]
   147  
   148  		if strings.HasPrefix(envKey, "TF_VAR_") {
   149  			log.Printf("[INFO] TF_VAR_* environment variable found: key=%s", envKey)
   150  			varName := strings.Replace(envKey, "TF_VAR_", "", 1)
   151  
   152  			envVariables[varName] = &terraform.InputValue{
   153  				Value:      cty.StringVal(envVal),
   154  				SourceType: terraform.ValueFromEnvVar,
   155  			}
   156  		}
   157  	}
   158  
   159  	return envVariables
   160  }