From 7930fef2615eebd5d916a2abf7ba34b3bdd4d92b Mon Sep 17 00:00:00 2001 From: Han Zhou Date: Tue, 8 Oct 2019 00:28:19 +0900 Subject: [PATCH] feat(tf,plan): extract action part from terraform plan output. **Why** Sometimes we found the part about `Refreshing Terraform state in-memory prior to plan` has too many lines that we do not need to care about in the code review. So make this change can help us to focus on the action part that terraform plan is. --- README.md | 1 + terraform/parser.go | 24 ++++++++++++++++++------ terraform/parser_test.go | 16 +++++++++++++++- terraform/template.go | 7 ++++--- 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 07faf19..d0f30d2 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ Placeholder | Usage ---|--- `{{ .Title }}` | Like `## Plan result` `{{ .Message }}` | A string that can be set from CLI with `--message` option +`{{ .Action }}` | Using in terraform plan, and matched leading message by parsing like `Terraform will perform the following actions:` `{{ .Result }}` | Matched result by parsing like `Plan: 1 to add` or `No changes` `{{ .Body }}` | The entire of Terraform execution result `{{ .Link }}` | The link of the build page on CI diff --git a/terraform/parser.go b/terraform/parser.go index 39ea6e4..835c387 100644 --- a/terraform/parser.go +++ b/terraform/parser.go @@ -14,6 +14,7 @@ type Parser interface { // ParseResult represents the result of parsed terraform execution type ParseResult struct { Result string + Action string ExitCode int Error error } @@ -30,8 +31,9 @@ type FmtParser struct { // PlanParser is a parser for terraform plan type PlanParser struct { - Pass *regexp.Regexp - Fail *regexp.Regexp + Pass *regexp.Regexp + Action *regexp.Regexp + Fail *regexp.Regexp } // ApplyParser is a parser for terraform apply @@ -55,8 +57,9 @@ func NewFmtParser() *FmtParser { // NewPlanParser is PlanParser initialized with its Regexp func NewPlanParser() *PlanParser { return &PlanParser{ - Pass: regexp.MustCompile(`(?m)^(Plan: \d|No changes.)`), - Fail: regexp.MustCompile(`(?m)^(Error: )`), + Pass: regexp.MustCompile(`(?m)^(Plan: \d|No changes.)`), + Action: regexp.MustCompile(`(?m)^(An execution plan has been generated)`), + Fail: regexp.MustCompile(`(?m)^(Error: )`), } } @@ -103,9 +106,13 @@ func (p *PlanParser) Parse(body string) ParseResult { } } lines := strings.Split(body, "\n") - var i int - var result, line string + var i, actionStartIdx int + var result, action, line string for i, line = range lines { + if p.Action.MatchString(line) { + // action starts with the line: An execution plan... + actionStartIdx = i + } if p.Pass.MatchString(line) || p.Fail.MatchString(line) { break } @@ -113,11 +120,16 @@ func (p *PlanParser) Parse(body string) ParseResult { switch { case p.Pass.MatchString(line): result = lines[i] + if actionStartIdx != -1 { + // action ends with the line above summary + action = strings.Join(trimLastNewline(lines[actionStartIdx:i]), "\n") + } case p.Fail.MatchString(line): result = strings.Join(trimLastNewline(lines[i:]), "\n") } return ParseResult{ Result: result, + Action: action, ExitCode: exitCode, Error: nil, } diff --git a/terraform/parser_test.go b/terraform/parser_test.go index be341d4..7934c62 100644 --- a/terraform/parser_test.go +++ b/terraform/parser_test.go @@ -251,7 +251,21 @@ func TestPlanParserParse(t *testing.T) { name: "plan ok pattern", body: planSuccessResult, result: ParseResult{ - Result: "Plan: 1 to add, 0 to change, 0 to destroy.", + Result: "Plan: 1 to add, 0 to change, 0 to destroy.", + Action: `An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + google_compute_global_address.my_another_project + id: + address: + ip_version: "IPV4" + name: "my-another-project" + project: "my-project" + self_link: +`, ExitCode: 0, Error: nil, }, diff --git a/terraform/template.go b/terraform/template.go index ca92f51..7632a65 100644 --- a/terraform/template.go +++ b/terraform/template.go @@ -182,7 +182,7 @@ func (t *DefaultTemplate) Execute() (resp string, err error) { return resp, err } -// Execute binds the execution result of terraform fmt into tepmlate +// Execute binds the execution result of terraform fmt into template func (t *FmtTemplate) Execute() (resp string, err error) { tpl, err := template.New("fmt").Parse(t.Template) if err != nil { @@ -202,7 +202,7 @@ func (t *FmtTemplate) Execute() (resp string, err error) { return resp, err } -// Execute binds the execution result of terraform plan into tepmlate +// Execute binds the execution result of terraform plan into template func (t *PlanTemplate) Execute() (resp string, err error) { tpl, err := template.New("plan").Parse(t.Template) if err != nil { @@ -212,6 +212,7 @@ func (t *PlanTemplate) Execute() (resp string, err error) { if err := tpl.Execute(&b, map[string]interface{}{ "Title": t.Title, "Message": t.Message, + "Action": t.Action, "Result": t.Result, "Body": t.Body, "Link": t.Link, @@ -222,7 +223,7 @@ func (t *PlanTemplate) Execute() (resp string, err error) { return resp, err } -// Execute binds the execution result of terraform apply into tepmlate +// Execute binds the execution result of terraform apply into template func (t *ApplyTemplate) Execute() (resp string, err error) { tpl, err := template.New("apply").Parse(t.Template) if err != nil {