Skip to content

Commit 7301feb

Browse files
authored
fix(output): Remove Refreshing state... from output (#1352)
* fix(output): Remove Refreshing state... from output Since Terraform 0.14.0 there are no separator between refreshing plan and the plan. * Fix comment typo
1 parent 00a5bc0 commit 7301feb

File tree

3 files changed

+104
-28
lines changed

3 files changed

+104
-28
lines changed

server/events/runtime/apply_step_runner.go

+7-11
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func (a *ApplyStepRunner) cleanRemoteApplyOutput(out string) string {
9090
applyStartText := ` Terraform will perform the actions described above.
9191
Only 'yes' will be accepted to approve.
9292
93-
Enter a value:
93+
Enter a value:
9494
`
9595
applyStartIdx := strings.Index(out, applyStartText)
9696
if applyStartIdx < 0 {
@@ -162,7 +162,7 @@ func (a *ApplyStepRunner) runRemoteApply(
162162
ctx.Log.Debug("remote apply is waiting for confirmation")
163163

164164
// Check if the plan is as expected.
165-
planChangedErr = a.remotePlanChanged(string(planfileBytes), strings.Join(lines, "\n"))
165+
planChangedErr = a.remotePlanChanged(string(planfileBytes), strings.Join(lines, "\n"), tfVersion)
166166
if planChangedErr != nil {
167167
ctx.Log.Err("plan generated during apply does not match expected plan, aborting")
168168
inCh <- "no\n"
@@ -198,19 +198,15 @@ func (a *ApplyStepRunner) runRemoteApply(
198198
// the one we're about to apply in the apply phase.
199199
// If the plans don't match, it returns an error with a diff of the two plans
200200
// that can be printed to the pull request.
201-
func (a *ApplyStepRunner) remotePlanChanged(planfileContents string, applyOut string) error {
202-
// The plan is between the refresh separator...
203-
planStartIdx := strings.Index(applyOut, refreshSeparator)
204-
if planStartIdx < 0 {
205-
return fmt.Errorf("Couldn't find refresh separator when parsing apply output:\n%q", applyOut)
206-
}
201+
func (a *ApplyStepRunner) remotePlanChanged(planfileContents string, applyOut string, tfVersion *version.Version) error {
202+
output := StripRefreshingFromPlanOutput(applyOut, tfVersion)
207203

208-
// ...and the prompt to execute the plan.
209-
planEndIdx := strings.Index(applyOut, "Do you want to perform these actions in workspace \"")
204+
// Strip plan output after the prompt to execute the plan.
205+
planEndIdx := strings.Index(output, "Do you want to perform these actions in workspace \"")
210206
if planEndIdx < 0 {
211207
return fmt.Errorf("Couldn't find plan end when parsing apply output:\n%q", applyOut)
212208
}
213-
currPlan := strings.TrimSpace(applyOut[planStartIdx+len(refreshSeparator) : planEndIdx])
209+
currPlan := strings.TrimSpace(output[: planEndIdx])
214210

215211
// Ensure we strip the remoteOpsHeader from the plan contents so the
216212
// comparison is fair. We add this header in the plan phase so we can

server/events/runtime/plan_step_runner.go

+32-17
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import (
1515

1616
const (
1717
defaultWorkspace = "default"
18-
// refreshSeparator is what separates the refresh stage from the calculated
19-
// plan during a terraform plan.
18+
refreshKeyword = "Refreshing state..."
2019
refreshSeparator = "------------------------------------------------------------------------\n"
2120
)
2221

@@ -55,7 +54,7 @@ func (p *PlanStepRunner) Run(ctx models.ProjectCommandContext, extraArgs []strin
5554
if err != nil {
5655
return output, err
5756
}
58-
return p.fmtPlanOutput(output), nil
57+
return p.fmtPlanOutput(output, tfVersion), nil
5958
}
6059

6160
// isRemoteOpsErr returns true if there was an error caused due to this
@@ -89,11 +88,7 @@ func (p *PlanStepRunner) remotePlan(ctx models.ProjectCommandContext, extraArgs
8988
// plan. To ensure that what gets applied is the plan we printed to the PR,
9089
// during the apply phase, we diff the output we stored in the fake
9190
// planfile with the pending apply output.
92-
planOutput := output
93-
sepIdx := strings.Index(planOutput, refreshSeparator)
94-
if sepIdx > -1 {
95-
planOutput = planOutput[sepIdx+len(refreshSeparator):]
96-
}
91+
planOutput := StripRefreshingFromPlanOutput(output, tfVersion)
9792

9893
// We also prepend our own remote ops header to the file so during apply we
9994
// know this is a remote apply.
@@ -102,7 +97,7 @@ func (p *PlanStepRunner) remotePlan(ctx models.ProjectCommandContext, extraArgs
10297
return output, errors.Wrap(err, "unable to create planfile for remote ops")
10398
}
10499

105-
return p.fmtPlanOutput(output), nil
100+
return p.fmtPlanOutput(output, tfVersion), nil
106101
}
107102

108103
// switchWorkspace changes the terraform workspace if necessary and will create
@@ -228,14 +223,8 @@ func (p *PlanStepRunner) flatten(slices [][]string) []string {
228223
// "- aws_security_group_rule.allow_all"
229224
// We do it for +, ~ and -.
230225
// It also removes the "Refreshing..." preamble.
231-
func (p *PlanStepRunner) fmtPlanOutput(output string) string {
232-
// Plan output contains a lot of "Refreshing..." lines followed by a
233-
// separator. We want to remove everything before that separator.
234-
sepIdx := strings.Index(output, refreshSeparator)
235-
if sepIdx > -1 {
236-
output = output[sepIdx+len(refreshSeparator):]
237-
}
238-
226+
func (p *PlanStepRunner) fmtPlanOutput(output string, tfVersion *version.Version) string {
227+
output = StripRefreshingFromPlanOutput(output, tfVersion)
239228
output = plusDiffRegex.ReplaceAllString(output, "+")
240229
output = tildeDiffRegex.ReplaceAllString(output, "~")
241230
return minusDiffRegex.ReplaceAllString(output, "-")
@@ -299,6 +288,32 @@ func (p *PlanStepRunner) runRemotePlan(
299288
return output, err
300289
}
301290

291+
func StripRefreshingFromPlanOutput(output string, tfVersion *version.Version) string {
292+
if tfVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.14.0"))) {
293+
// Plan output contains a lot of "Refreshing..." lines, remove it
294+
lines := strings.Split(output, "\n")
295+
finalIndex := 0
296+
for i, line := range lines {
297+
if strings.Contains(line, refreshKeyword) {
298+
finalIndex = i
299+
}
300+
}
301+
302+
if finalIndex != 0 {
303+
output = strings.Join(lines[finalIndex + 1:], "\n")
304+
}
305+
return output
306+
} else {
307+
// Plan output contains a lot of "Refreshing..." lines followed by a
308+
// separator. We want to remove everything before that separator.
309+
sepIdx := strings.Index(output, refreshSeparator)
310+
if sepIdx > -1 {
311+
output = output[sepIdx+len(refreshSeparator):]
312+
}
313+
return output
314+
}
315+
}
316+
302317
// remoteOpsErr01114 is the error terraform plan will return if this project is
303318
// using TFE remote operations in TF 0.11.14.
304319
var remoteOpsErr01114 = `Error: Saving a generated plan is currently not supported!

server/events/runtime/plan_step_runner_test.go

+65
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,71 @@ Plan: 0 to add, 0 to change, 1 to destroy.`, string(bytes))
814814
}
815815
}
816816

817+
// Test striping output method
818+
func TestStripRefreshingFromPlanOutput(t *testing.T) {
819+
tfVersion_0135, _ := version.NewVersion("0.13.5")
820+
tfVersion_0140, _ := version.NewVersion("0.14.0")
821+
cases := []struct {
822+
out string
823+
tfVersion *version.Version
824+
}{
825+
{
826+
remotePlanOutput,
827+
tfVersion_0135,
828+
},
829+
{
830+
`Running plan in the remote backend. Output will stream here. Pressing Ctrl-C
831+
will stop streaming the logs, but will not stop the plan running remotely.
832+
833+
Preparing the remote plan...
834+
835+
To view this run in a browser, visit:
836+
https://app.terraform.io/app/lkysow-enterprises/atlantis-tfe-test/runs/run-is4oVvJfrkud1KvE
837+
838+
Waiting for the plan to start...
839+
840+
Terraform v0.14.0
841+
842+
Configuring remote state backend...
843+
Initializing Terraform configuration...
844+
2019/02/20 22:40:52 [DEBUG] Using modified User-Agent: Terraform/0.14.0TFE/202eeff
845+
Refreshing Terraform state in-memory prior to plan...
846+
The refreshed state will be used to calculate this plan, but will not be
847+
persisted to local or remote state storage.
848+
849+
null_resource.hi: Refreshing state... (ID: 217661332516885645)
850+
null_resource.hi[1]: Refreshing state... (ID: 6064510335076839362)
851+
852+
An execution plan has been generated and is shown below.
853+
Resource actions are indicated with the following symbols:
854+
- destroy
855+
856+
Terraform will perform the following actions:
857+
858+
- null_resource.hi[1]
859+
860+
861+
Plan: 0 to add, 0 to change, 1 to destroy.`,
862+
tfVersion_0140,
863+
},
864+
}
865+
866+
for _, c := range cases {
867+
output := runtime.StripRefreshingFromPlanOutput(c.out, c.tfVersion)
868+
Equals(t, `
869+
An execution plan has been generated and is shown below.
870+
Resource actions are indicated with the following symbols:
871+
- destroy
872+
873+
Terraform will perform the following actions:
874+
875+
- null_resource.hi[1]
876+
877+
878+
Plan: 0 to add, 0 to change, 1 to destroy.`, output)
879+
}
880+
}
881+
817882
type remotePlanMock struct {
818883
// LinesToSend will be sent on the channel.
819884
LinesToSend string

0 commit comments

Comments
 (0)