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 }