Skip to content

Commit

Permalink
Merge pull request #125 from albertorm95/support-validate
Browse files Browse the repository at this point in the history
Feat support validate terraform command
  • Loading branch information
drlau authored Dec 14, 2022
2 parents 1a2db90 + b01673c commit e60974f
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 7 deletions.
18 changes: 12 additions & 6 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@ type TypetalkNotifier struct {

// Terraform represents terraform configurations
type Terraform struct {
Default Default `yaml:"default"`
Fmt Fmt `yaml:"fmt"`
Plan Plan `yaml:"plan"`
Apply Apply `yaml:"apply"`
UseRawOutput bool `yaml:"use_raw_output,omitempty"`
Default Default `yaml:"default"`
Fmt Fmt `yaml:"fmt"`
Plan Plan `yaml:"plan"`
Apply Apply `yaml:"apply"`
UseRawOutput bool `yaml:"use_raw_output,omitempty"`
Validate Validate `yaml:"validate"`
}

// Default is a default setting for terraform commands
Expand All @@ -79,6 +80,11 @@ type Fmt struct {
Template string `yaml:"template"`
}

// Validate is a terraform validate config
type Validate struct {
Template string `yaml:"template"`
}

// Plan is a terraform plan config
type Plan struct {
Template string `yaml:"template"`
Expand Down Expand Up @@ -174,7 +180,7 @@ func (cfg *Config) Validation() error {
}
if cfg.isDefinedTypetalk() {
if cfg.Notifier.Typetalk.TopicID == "" {
return fmt.Errorf("Typetalk topic id is missing")
return fmt.Errorf("typetalk topic id is missing")
}
}
notifier := cfg.GetNotifierType()
Expand Down
11 changes: 10 additions & 1 deletion config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ func TestLoadFile(t *testing.T) {
Fmt: Fmt{
Template: "",
},
Validate: Validate{
Template: "",
},
Plan: Plan{
Template: "{{ .Title }}\n{{ .Message }}\n{{if .Result}}\n<pre><code>{{ .Result }}\n</pre></code>\n{{end}}\n<details><summary>Details (Click me)</summary>\n\n<pre><code>{{ .Body }}\n</pre></code></details>\n",
WhenDestroy: WhenDestroy{},
Expand Down Expand Up @@ -91,6 +94,9 @@ func TestLoadFile(t *testing.T) {
Fmt: Fmt{
Template: "",
},
Validate: Validate{
Template: "",
},
Plan: Plan{
Template: "{{ .Title }}\n{{ .Message }}\n{{if .Result}}\n<pre><code>{{ .Result }}\n</pre></code>\n{{end}}\n<details><summary>Details (Click me)</summary>\n\n<pre><code>{{ .Body }}\n</pre></code></details>\n",
WhenAddOrUpdateOnly: WhenAddOrUpdateOnly{
Expand Down Expand Up @@ -145,6 +151,9 @@ func TestLoadFile(t *testing.T) {
Fmt: Fmt{
Template: "",
},
Validate: Validate{
Template: "",
},
Plan: Plan{
Template: "{{ .Title }}\n{{ .Message }}\n{{if .Result}}\n<pre><code>{{ .Result }}\n</pre></code>\n{{end}}\n<details><summary>Details (Click me)</summary>\n\n<pre><code>{{ .Body }}\n</pre></code></details>\n",
WhenDestroy: WhenDestroy{},
Expand Down Expand Up @@ -342,7 +351,7 @@ notifier:
typetalk:
token: token
`),
expected: "Typetalk topic id is missing",
expected: "typetalk topic id is missing",
},
{
contents: []byte(`
Expand Down
29 changes: 29 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,21 @@ func main() {
},
},
},
{
Name: "validate",
Usage: "Parse stdin as a validate result",
Action: cmdValidate,
Flags: []cli.Flag{
cli.StringFlag{
Name: "title, t",
Usage: "Specify the title to use for notification",
},
cli.StringFlag{
Name: "message, m",
Usage: "Specify the message to use for notification",
},
},
},
{
Name: "plan",
Usage: "Parse stdin as a plan result",
Expand Down Expand Up @@ -291,6 +306,20 @@ func cmdFmt(ctx *cli.Context) error {
return t.Run()
}

func cmdValidate(ctx *cli.Context) error {
cfg, err := newConfig(ctx)
if err != nil {
return err
}
t := &tfnotify{
config: cfg,
context: ctx,
parser: terraform.NewValidateParser(),
template: terraform.NewValidateTemplate(cfg.Terraform.Validate.Template),
}
return t.Run()
}

func cmdPlan(ctx *cli.Context) error {
cfg, err := newConfig(ctx)
if err != nil {
Expand Down
23 changes: 23 additions & 0 deletions terraform/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ type FmtParser struct {
Fail *regexp.Regexp
}

// ValidateParser is a parser for terraform Validate
type ValidateParser struct {
Pass *regexp.Regexp
Fail *regexp.Regexp
}

// PlanParser is a parser for terraform plan
type PlanParser struct {
Pass *regexp.Regexp
Expand All @@ -58,6 +64,13 @@ func NewFmtParser() *FmtParser {
}
}

// NewValidateParser is ValidateParser initialized with its Regexp
func NewValidateParser() *ValidateParser {
return &ValidateParser{
Fail: regexp.MustCompile(`(?m)^(│\s{1})?(Error: )`),
}
}

// NewPlanParser is PlanParser initialized with its Regexp
func NewPlanParser() *PlanParser {
return &PlanParser{
Expand Down Expand Up @@ -96,6 +109,16 @@ func (p *FmtParser) Parse(body string) ParseResult {
return result
}

// Parse returns ParseResult related with terraform validate
func (p *ValidateParser) Parse(body string) ParseResult {
result := ParseResult{}
if p.Fail.MatchString(body) {
result.Result = "There is a validation error in your Terraform code"
result.ExitCode = ExitFail
}
return result
}

// Parse returns ParseResult related with terraform plan
func (p *PlanParser) Parse(body string) ParseResult {
var exitCode int
Expand Down
62 changes: 62 additions & 0 deletions terraform/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,26 @@ versions.tf
}
`

// terraform validate
const validateFailResult0_11 = `
Error:
Terraform doesn't allow running any operations against a state
that was written by a future Terraform version. The state is
reporting it is written by Terraform '0.11.15'
A newer version of Terraform is required to make changes to the current
workspace.
`

const validateFailResult0_11_second = `
xxxxxxxxx
xxxxxxxxx
xxxxxxxxx
Error: module 'iam-profile': unknown resource 'data.aws_iam_policy_document.policy' referenced in variable data.aws_iam_policy_document.policy.json
`

const planSuccessResult = `
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
Expand Down Expand Up @@ -514,6 +534,48 @@ func TestFmtParserParse(t *testing.T) {
}
}

func TestValidateParserParse(t *testing.T) {
testCases := []struct {
name string
body string
result ParseResult
}{
{
name: "error",
body: validateFailResult0_11,
result: ParseResult{
Result: "There is a validation error in your Terraform code",
ExitCode: 1,
Error: nil,
},
},
{
name: "error_2",
body: validateFailResult0_11_second,
result: ParseResult{
Result: "There is a validation error in your Terraform code",
ExitCode: 1,
Error: nil,
},
},
{
name: "no stdin",
body: "",
result: ParseResult{
Result: "",
ExitCode: 0,
Error: nil,
},
},
}
for _, testCase := range testCases {
result := NewValidateParser().Parse(testCase.body)
if !reflect.DeepEqual(result, testCase.result) {
t.Errorf("got %v but want %v", result, testCase.result)
}
}
}

func TestPlanParserParse(t *testing.T) {
testCases := []struct {
name string
Expand Down
67 changes: 67 additions & 0 deletions terraform/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const (
DefaultDefaultTitle = "## Terraform result"
// DefaultFmtTitle is a default title for terraform fmt
DefaultFmtTitle = "## Fmt result"
// DefaultValidateTitle is a default title for terraform validate
DefaultValidateTitle = "## Validate result"
// DefaultPlanTitle is a default title for terraform plan
DefaultPlanTitle = "## Plan result"
// DefaultDestroyWarningTitle is a default title of destroy warning
Expand Down Expand Up @@ -48,6 +50,23 @@ const (
<details><summary>Details (Click me)</summary>
<pre><code>{{ .Body }}
</code></pre></details>
`

// DefaultValidateTemplate is a default template for terraform validate
DefaultValidateTemplate = `
{{ .Title }}
{{ .Message }}
{{if .Result}}
<pre><code>{{ .Result }}
</code></pre>
{{end}}
<details><summary>Details (Click me)</summary>
<pre><code>{{ .Body }}
</code></pre></details>
`
Expand Down Expand Up @@ -130,6 +149,13 @@ type FmtTemplate struct {
CommonTemplate
}

// ValidateTemplate is a default template for terraform validate
type ValidateTemplate struct {
Template string

CommonTemplate
}

// PlanTemplate is a default template for terraform plan
type PlanTemplate struct {
Template string
Expand Down Expand Up @@ -171,6 +197,16 @@ func NewFmtTemplate(template string) *FmtTemplate {
}
}

// NewValidateTemplate is ValidateTemplate initializer
func NewValidateTemplate(template string) *ValidateTemplate {
if template == "" {
template = DefaultValidateTemplate
}
return &ValidateTemplate{
Template: template,
}
}

// NewPlanTemplate is PlanTemplate initializer
func NewPlanTemplate(template string) *PlanTemplate {
if template == "" {
Expand Down Expand Up @@ -261,6 +297,24 @@ func (t *FmtTemplate) Execute() (string, error) {
return resp, nil
}

// Execute binds the execution result of terraform validate into template
func (t *ValidateTemplate) Execute() (string, error) {
data := map[string]interface{}{
"Title": t.Title,
"Message": t.Message,
"Result": t.Result,
"Body": t.Body,
"Link": t.Link,
}

resp, err := generateOutput("validate", t.Template, data, t.UseRawOutput)
if err != nil {
return "", err
}

return resp, nil
}

// Execute binds the execution result of terraform plan into template
func (t *PlanTemplate) Execute() (string, error) {
data := map[string]interface{}{
Expand Down Expand Up @@ -331,6 +385,14 @@ func (t *FmtTemplate) SetValue(ct CommonTemplate) {
t.CommonTemplate = ct
}

// SetValue sets template entities about terraform validate to CommonTemplate
func (t *ValidateTemplate) SetValue(ct CommonTemplate) {
if ct.Title == "" {
ct.Title = DefaultValidateTitle
}
t.CommonTemplate = ct
}

// SetValue sets template entities about terraform plan to CommonTemplate
func (t *PlanTemplate) SetValue(ct CommonTemplate) {
if ct.Title == "" {
Expand Down Expand Up @@ -365,6 +427,11 @@ func (t *FmtTemplate) GetValue() CommonTemplate {
return t.CommonTemplate
}

// GetValue gets template entities
func (t *ValidateTemplate) GetValue() CommonTemplate {
return t.CommonTemplate
}

// GetValue gets template entities
func (t *PlanTemplate) GetValue() CommonTemplate {
return t.CommonTemplate
Expand Down
Loading

0 comments on commit e60974f

Please sign in to comment.