github.com/jpreese/tflint@v0.19.2-0.20200908152133-b01686250fb6/rules/awsrules/models/generator/rule.go (about)

     1  // +build generators
     2  
     3  package main
     4  
     5  import (
     6  	"fmt"
     7  	"regexp"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
    11  	utils "github.com/terraform-linters/tflint/rules/awsrules/generator-utils"
    12  )
    13  
    14  type ruleMeta struct {
    15  	RuleName      string
    16  	RuleNameCC    string
    17  	ResourceType  string
    18  	AttributeName string
    19  	Sensitive     bool
    20  	Max           int
    21  	Min           int
    22  	Pattern       string
    23  	Enum          []string
    24  	TestOK        string
    25  	TestNG        string
    26  }
    27  
    28  func generateRuleFile(resource, attribute string, model map[string]interface{}, schema *schema.Schema) {
    29  	ruleName := makeRuleName(resource, attribute)
    30  
    31  	meta := &ruleMeta{
    32  		RuleName:      ruleName,
    33  		RuleNameCC:    utils.ToCamel(ruleName),
    34  		ResourceType:  resource,
    35  		AttributeName: attribute,
    36  		Sensitive:     schema.Sensitive,
    37  		Max:           fetchNumber(model, "max"),
    38  		Min:           fetchNumber(model, "min"),
    39  		Pattern:       replacePattern(fetchString(model, "pattern")),
    40  		Enum:          fetchStrings(model, "enum"),
    41  	}
    42  
    43  	// Testing generated regexp
    44  	regexp.MustCompile(meta.Pattern)
    45  
    46  	utils.GenerateFile(fmt.Sprintf("%s.go", ruleName), "pattern_rule.go.tmpl", meta)
    47  }
    48  
    49  func generateRuleTestFile(resource, attribute string, model map[string]interface{}, test test) {
    50  	ruleName := makeRuleName(resource, attribute)
    51  
    52  	meta := &ruleMeta{
    53  		RuleName:      ruleName,
    54  		RuleNameCC:    utils.ToCamel(ruleName),
    55  		ResourceType:  resource,
    56  		AttributeName: attribute,
    57  		Max:           fetchNumber(model, "max"),
    58  		Min:           fetchNumber(model, "min"),
    59  		Pattern:       replacePattern(fetchString(model, "pattern")),
    60  		Enum:          fetchStrings(model, "enum"),
    61  		TestOK:        formatTest(test.OK),
    62  		TestNG:        formatTest(test.NG),
    63  	}
    64  
    65  	// Testing generated regexp
    66  	regexp.MustCompile(meta.Pattern)
    67  
    68  	utils.GenerateFile(fmt.Sprintf("%s_test.go", ruleName), "pattern_rule_test.go.tmpl", meta)
    69  }
    70  
    71  func makeRuleName(resource, attribute string) string {
    72  	// XXX: Change the naming convention for the backward compatibility.
    73  	if resource == "aws_instance" && attribute == "instance_type" {
    74  		return "aws_instance_invalid_type"
    75  	}
    76  	if resource == "aws_launch_configuration" && attribute == "instance_type" {
    77  		return "aws_launch_configuration_invalid_type"
    78  	}
    79  	return fmt.Sprintf("%s_invalid_%s", resource, attribute)
    80  }
    81  
    82  func fetchNumber(model map[string]interface{}, key string) int {
    83  	if v, ok := model[key]; ok {
    84  		return int(v.(float64))
    85  	}
    86  	return 0
    87  }
    88  
    89  func fetchStrings(model map[string]interface{}, key string) []string {
    90  	if raw, ok := model[key]; ok {
    91  		list := raw.([]interface{})
    92  		ret := make([]string, len(list))
    93  		for i, v := range list {
    94  			ret[i] = v.(string)
    95  		}
    96  		return ret
    97  	}
    98  	return []string{}
    99  }
   100  
   101  func fetchString(model map[string]interface{}, key string) string {
   102  	if v, ok := model[key]; ok {
   103  		return v.(string)
   104  	}
   105  	return ""
   106  }
   107  
   108  func replacePattern(pattern string) string {
   109  	if pattern == "" {
   110  		return pattern
   111  	}
   112  	reg := regexp.MustCompile(`\\u([0-9A-F]{4})`)
   113  	replaced := reg.ReplaceAllString(pattern, `\x{$1}`)
   114  	if !strings.HasPrefix(replaced, "^") && !strings.HasSuffix(replaced, "$") {
   115  		return fmt.Sprintf("^%s$", replaced)
   116  	}
   117  	return replaced
   118  }
   119  
   120  func formatTest(body string) string {
   121  	if strings.Contains(body, "\n") {
   122  		return fmt.Sprintf("<<TEXT\n%sTEXT", body)
   123  	}
   124  	return fmt.Sprintf(`"%s"`, body)
   125  }