Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: parse error when only outputs is changed #896

Merged
merged 5 commits into from
Sep 9, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions pkg/terraform/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func NewDefaultParser() *DefaultParser {
// NewPlanParser is PlanParser initialized with its Regexp
func NewPlanParser() *PlanParser {
return &PlanParser{
Pass: regexp.MustCompile(`(?m)^(Plan: \d|No changes.)`),
Pass: regexp.MustCompile(`(?m)^(Plan: \d|No changes.|Changes to Outputs:)`),
Fail: regexp.MustCompile(`(?m)^(Error: )`),
// "0 to destroy" should be treated as "no destroy"
HasDestroy: regexp.MustCompile(`(?m)([1-9][0-9]* to destroy.)`),
Expand Down Expand Up @@ -150,8 +150,8 @@ func (p *PlanParser) Parse(body string) ParseResult { //nolint:cyclop
if line == "Terraform will perform the following actions:" { // https://github.com/hashicorp/terraform/blob/332045a4e4b1d256c45f98aac74e31102ace7af7/internal/command/views/plan.go#L252
startChangeOutput = i + 1
}
if startChangeOutput != -1 && endChangeOutput == -1 && strings.HasPrefix(line, "Plan: ") { // https://github.com/hashicorp/terraform/blob/dfc12a6a9e1cff323829026d51873c1b80200757/internal/command/views/plan.go#L306
endChangeOutput = i + 1
if startChangeOutput != -1 && endChangeOutput == -1 && strings.HasPrefix(line, "-----") { // https://github.com/hashicorp/terraform/blob/1ac7a37d00f3c796f816070847bf02109cb9cab2/internal/command/views/operation.go#L142
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To include changes to outputs coming after Plan:, we have to wait for a horizontal rule which comes after Changes to Outputs:.

endChangeOutput = i - 1
}
if strings.HasPrefix(line, "Warning:") && startWarning == -1 {
startWarning = i
Expand Down Expand Up @@ -201,6 +201,10 @@ func (p *PlanParser) Parse(body string) ParseResult { //nolint:cyclop

changeResult := ""
if startChangeOutput != -1 {
// if we get here before finding a horizontal rule, output all remaining.
if endChangeOutput == -1 {
endChangeOutput = len(lines) - 1
}
Comment on lines +212 to +215
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If TF_IN_AUTOMATION environment variable is set, no horizontal rules appear after Terraform will perform the following actions:. 😢

changeResult = strings.Join(lines[startChangeOutput:endChangeOutput], "\n")
}

Expand Down
154 changes: 154 additions & 0 deletions pkg/terraform/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,110 @@ can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
`

const planOnlyOutputChangesSuccessResult0_12 = `
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

data.terraform_remote_state.teams_platform_development: Refreshing state...
google_project.my_project: Refreshing state...
aws_iam_policy.datadog_aws_integration: Refreshing state...
aws_iam_user.teams_terraform: Refreshing state...
aws_iam_role.datadog_aws_integration: Refreshing state...
google_project_services.my_project: Refreshing state...
google_bigquery_dataset.gateway_access_log: Refreshing state...
aws_iam_role_policy_attachment.datadog_aws_integration: Refreshing state...
google_logging_project_sink.gateway_access_log_bigquery_sink: Refreshing state...
google_project_iam_member.gateway_access_log_bigquery_sink_writer_is_bigquery_data_editor: Refreshing state...
google_dns_managed_zone.tfnotifyapps_com: Refreshing state...
google_dns_record_set.dev_tfnotifyapps_com: Refreshing state...

------------------------------------------------------------------------

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:

Plan: 0 to add, 0 to change, 0 to destroy.

Changes to Outputs:
+ aws_instance_name = "my-instance"

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
`

const planOnlyOutputChangesSuccessResult0_15 = `
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

data.terraform_remote_state.teams_platform_development: Refreshing state...
google_project.my_project: Refreshing state...
aws_iam_policy.datadog_aws_integration: Refreshing state...
aws_iam_user.teams_terraform: Refreshing state...
aws_iam_role.datadog_aws_integration: Refreshing state...
google_project_services.my_project: Refreshing state...
google_bigquery_dataset.gateway_access_log: Refreshing state...
aws_iam_role_policy_attachment.datadog_aws_integration: Refreshing state...
google_logging_project_sink.gateway_access_log_bigquery_sink: Refreshing state...
google_project_iam_member.gateway_access_log_bigquery_sink_writer_is_bigquery_data_editor: Refreshing state...
google_dns_managed_zone.tfnotifyapps_com: Refreshing state...
google_dns_record_set.dev_tfnotifyapps_com: Refreshing state...

------------------------------------------------------------------------

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:

Changes to Outputs:
+ aws_instance_name = "my-instance"

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
`

const planOnlyOutputChangesSuccessInAutomationResult = `
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

data.terraform_remote_state.teams_platform_development: Refreshing state...
google_project.my_project: Refreshing state...
aws_iam_policy.datadog_aws_integration: Refreshing state...
aws_iam_user.teams_terraform: Refreshing state...
aws_iam_role.datadog_aws_integration: Refreshing state...
google_project_services.my_project: Refreshing state...
google_bigquery_dataset.gateway_access_log: Refreshing state...
aws_iam_role_policy_attachment.datadog_aws_integration: Refreshing state...
google_logging_project_sink.gateway_access_log_bigquery_sink: Refreshing state...
google_project_iam_member.gateway_access_log_bigquery_sink_writer_is_bigquery_data_editor: Refreshing state...
google_dns_managed_zone.tfnotifyapps_com: Refreshing state...
google_dns_record_set.dev_tfnotifyapps_com: Refreshing state...

------------------------------------------------------------------------

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:

Changes to Outputs:
+ aws_instance_name = "my-instance"
`

const planFailureResult = `
xxxxxxxxx
xxxxxxxxx
Expand Down Expand Up @@ -351,6 +455,56 @@ func TestPlanParserParse(t *testing.T) {
Plan: 1 to add, 0 to change, 0 to destroy.`,
},
},
{
name: "plan output changes only pattern 0.12",
body: planOnlyOutputChangesSuccessResult0_12,
result: ParseResult{
Result: "Plan: 0 to add, 0 to change, 0 to destroy.",
Copy link
Contributor Author

@taro-kayo taro-kayo Sep 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not ideal, but it's very old version... 🙈

HasAddOrUpdateOnly: true,
HasDestroy: false,
HasNoChanges: false,
HasPlanError: false,
ExitCode: 0,
Error: nil,
ChangedResult: `
Plan: 0 to add, 0 to change, 0 to destroy.

Changes to Outputs:
+ aws_instance_name = "my-instance"`,
},
},
{
name: "plan output changes only pattern 0.15",
body: planOnlyOutputChangesSuccessResult0_15,
result: ParseResult{
Result: "Changes to Outputs:",
HasAddOrUpdateOnly: true,
HasDestroy: false,
HasNoChanges: false,
HasPlanError: false,
ExitCode: 0,
Error: nil,
ChangedResult: `
Changes to Outputs:
+ aws_instance_name = "my-instance"`,
},
},
{
name: "plan output changes only pattern with TF_IN_AUTOMATION",
body: planOnlyOutputChangesSuccessInAutomationResult,
result: ParseResult{
Result: "Changes to Outputs:",
HasAddOrUpdateOnly: true,
HasDestroy: false,
HasNoChanges: false,
HasPlanError: false,
ExitCode: 0,
Error: nil,
ChangedResult: `
Changes to Outputs:
+ aws_instance_name = "my-instance"`,
},
},
{
name: "no stdin",
body: "",
Expand Down