diff --git a/config/config.go b/config/config.go index b80f79c..0933f4b 100644 --- a/config/config.go +++ b/config/config.go @@ -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 @@ -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"` @@ -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() diff --git a/main.go b/main.go index 505c9d7..ee7df29 100644 --- a/main.go +++ b/main.go @@ -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", @@ -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 { diff --git a/terraform/parser.go b/terraform/parser.go index 938afc4..27931be 100644 --- a/terraform/parser.go +++ b/terraform/parser.go @@ -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 @@ -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{ @@ -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 diff --git a/terraform/template.go b/terraform/template.go index 408888d..5707c00 100644 --- a/terraform/template.go +++ b/terraform/template.go @@ -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 @@ -48,6 +50,23 @@ const (
Details (Click me) +
{{ .Body }}
+
+` + + // DefaultValidateTemplate is a default template for terraform validate + DefaultValidateTemplate = ` +{{ .Title }} + +{{ .Message }} + +{{if .Result}} +
{{ .Result }}
+
+{{end}} + +
Details (Click me) +
{{ .Body }}
 
` @@ -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 @@ -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 == "" { @@ -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{}{ @@ -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 == "" { @@ -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