mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-08-05 08:58:29 +00:00
Detect remote ops for TF >= 0.12.0 (#705)
The error message we use to detect remote ops changed between 0.11.14 and 0.12.0. Recognize this new error message.
This commit is contained in:
@@ -64,7 +64,7 @@ func (p *PlanStepRunner) isRemoteOpsErr(output string, err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(output, remoteOpsErr)
|
||||
return strings.Contains(output, remoteOpsErr01114) || strings.Contains(output, remoteOpsErr012)
|
||||
}
|
||||
|
||||
// remotePlan runs a terraform plan command compatible with TFE remote
|
||||
@@ -298,15 +298,25 @@ func (p *PlanStepRunner) runRemotePlan(
|
||||
|
||||
var vTwelveAndUp = MustConstraint(">=0.12-a")
|
||||
|
||||
// remoteOpsErr is the error terraform plan will return if this project is
|
||||
// using TFE remote operations.
|
||||
var remoteOpsErr = `Error: Saving a generated plan is currently not supported!
|
||||
// remoteOpsErr01114 is the error terraform plan will return if this project is
|
||||
// using TFE remote operations in TF 0.11.14.
|
||||
var remoteOpsErr01114 = `Error: Saving a generated plan is currently not supported!
|
||||
|
||||
The "remote" backend does not support saving the generated execution
|
||||
plan locally at this time.
|
||||
|
||||
`
|
||||
|
||||
// remoteOpsErr012 is the error terraform plan will return if this project is
|
||||
// using TFE remote operations in TF 0.12.{0-4}. Later versions haven't been
|
||||
// released yet at this time.
|
||||
var remoteOpsErr012 = `Error: Saving a generated plan is currently not supported
|
||||
|
||||
The "remote" backend does not support saving the generated execution plan
|
||||
locally at this time.
|
||||
|
||||
`
|
||||
|
||||
// remoteOpsHeader is the header we add to the planfile if this plan was
|
||||
// generated using TFE remote operations.
|
||||
var remoteOpsHeader = "Atlantis: this plan was created by remote ops\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
version "github.com/hashicorp/go-version"
|
||||
"github.com/hashicorp/go-version"
|
||||
mocks2 "github.com/runatlantis/atlantis/server/events/mocks"
|
||||
"github.com/runatlantis/atlantis/server/events/terraform"
|
||||
|
||||
@@ -659,82 +659,93 @@ func TestRun_NoOptionalVarsIn012(t *testing.T) {
|
||||
|
||||
// Test plans if using remote ops.
|
||||
func TestRun_RemoteOps(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
terraform := mocks.NewMockClient()
|
||||
asyncTf := &remotePlanMock{}
|
||||
|
||||
tfVersion, _ := version.NewVersion("0.11.12")
|
||||
updater := mocks2.NewMockCommitStatusUpdater()
|
||||
s := runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
DefaultTFVersion: tfVersion,
|
||||
AsyncTFExec: asyncTf,
|
||||
CommitStatusUpdater: updater,
|
||||
}
|
||||
absProjectPath, cleanup := TempDir(t)
|
||||
defer cleanup()
|
||||
|
||||
// First, terraform workspace gets run.
|
||||
When(terraform.RunCommandWithVersion(
|
||||
nil,
|
||||
absProjectPath,
|
||||
[]string{"workspace", "show"},
|
||||
tfVersion,
|
||||
"default")).ThenReturn("default\n", nil)
|
||||
|
||||
// Then the first call to terraform plan should return the remote ops error.
|
||||
expPlanArgs := []string{"plan",
|
||||
"-input=false",
|
||||
"-refresh",
|
||||
"-no-color",
|
||||
"-out",
|
||||
fmt.Sprintf("%q", filepath.Join(absProjectPath, "default.tfplan")),
|
||||
"-var",
|
||||
"atlantis_user=\"username\"",
|
||||
"-var",
|
||||
"atlantis_repo=\"owner/repo\"",
|
||||
"-var",
|
||||
"atlantis_repo_name=\"repo\"",
|
||||
"-var",
|
||||
"atlantis_repo_owner=\"owner\"",
|
||||
"-var",
|
||||
"atlantis_pull_num=2",
|
||||
"extra",
|
||||
"args",
|
||||
"comment",
|
||||
"args",
|
||||
}
|
||||
|
||||
planErr := errors.New("exit status 1: err")
|
||||
planOutput := `
|
||||
Error: Saving a generated plan is currently not supported!
|
||||
cases := map[string]string{
|
||||
"0.11.14 error": `Error: Saving a generated plan is currently not supported!
|
||||
|
||||
The "remote" backend does not support saving the generated execution
|
||||
plan locally at this time.
|
||||
|
||||
`
|
||||
asyncTf.LinesToSend = remotePlanOutput
|
||||
When(terraform.RunCommandWithVersion(nil, absProjectPath, expPlanArgs, tfVersion, "default")).
|
||||
ThenReturn(planOutput, planErr)
|
||||
`,
|
||||
"0.12.* error": `Error: Saving a generated plan is currently not supported
|
||||
|
||||
// Now that mocking is set up, we're ready to run the plan.
|
||||
ctx := models.ProjectCommandContext{
|
||||
Workspace: "default",
|
||||
RepoRelDir: ".",
|
||||
User: models.User{Username: "username"},
|
||||
EscapedCommentArgs: []string{"comment", "args"},
|
||||
Pull: models.PullRequest{
|
||||
Num: 2,
|
||||
},
|
||||
BaseRepo: models.Repo{
|
||||
FullName: "owner/repo",
|
||||
Owner: "owner",
|
||||
Name: "repo",
|
||||
},
|
||||
The "remote" backend does not support saving the generated execution plan
|
||||
locally at this time.
|
||||
|
||||
`,
|
||||
}
|
||||
output, err := s.Run(ctx, []string{"extra", "args"}, absProjectPath)
|
||||
Ok(t, err)
|
||||
Equals(t, `
|
||||
for name, remoteOpsErr := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
RegisterMockTestingT(t)
|
||||
terraform := mocks.NewMockClient()
|
||||
asyncTf := &remotePlanMock{}
|
||||
|
||||
tfVersion, _ := version.NewVersion("0.11.12")
|
||||
updater := mocks2.NewMockCommitStatusUpdater()
|
||||
s := runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
DefaultTFVersion: tfVersion,
|
||||
AsyncTFExec: asyncTf,
|
||||
CommitStatusUpdater: updater,
|
||||
}
|
||||
absProjectPath, cleanup := TempDir(t)
|
||||
defer cleanup()
|
||||
|
||||
// First, terraform workspace gets run.
|
||||
When(terraform.RunCommandWithVersion(
|
||||
nil,
|
||||
absProjectPath,
|
||||
[]string{"workspace", "show"},
|
||||
tfVersion,
|
||||
"default")).ThenReturn("default\n", nil)
|
||||
|
||||
// Then the first call to terraform plan should return the remote ops error.
|
||||
expPlanArgs := []string{"plan",
|
||||
"-input=false",
|
||||
"-refresh",
|
||||
"-no-color",
|
||||
"-out",
|
||||
fmt.Sprintf("%q", filepath.Join(absProjectPath, "default.tfplan")),
|
||||
"-var",
|
||||
"atlantis_user=\"username\"",
|
||||
"-var",
|
||||
"atlantis_repo=\"owner/repo\"",
|
||||
"-var",
|
||||
"atlantis_repo_name=\"repo\"",
|
||||
"-var",
|
||||
"atlantis_repo_owner=\"owner\"",
|
||||
"-var",
|
||||
"atlantis_pull_num=2",
|
||||
"extra",
|
||||
"args",
|
||||
"comment",
|
||||
"args",
|
||||
}
|
||||
|
||||
planErr := errors.New("exit status 1: err")
|
||||
planOutput := "\n" + remoteOpsErr
|
||||
asyncTf.LinesToSend = remotePlanOutput
|
||||
When(terraform.RunCommandWithVersion(nil, absProjectPath, expPlanArgs, tfVersion, "default")).
|
||||
ThenReturn(planOutput, planErr)
|
||||
|
||||
// Now that mocking is set up, we're ready to run the plan.
|
||||
ctx := models.ProjectCommandContext{
|
||||
Workspace: "default",
|
||||
RepoRelDir: ".",
|
||||
User: models.User{Username: "username"},
|
||||
EscapedCommentArgs: []string{"comment", "args"},
|
||||
Pull: models.PullRequest{
|
||||
Num: 2,
|
||||
},
|
||||
BaseRepo: models.Repo{
|
||||
FullName: "owner/repo",
|
||||
Owner: "owner",
|
||||
Name: "repo",
|
||||
},
|
||||
}
|
||||
output, err := s.Run(ctx, []string{"extra", "args"}, absProjectPath)
|
||||
Ok(t, err)
|
||||
Equals(t, `
|
||||
An execution plan has been generated and is shown below.
|
||||
Resource actions are indicated with the following symbols:
|
||||
- destroy
|
||||
@@ -746,13 +757,13 @@ Terraform will perform the following actions:
|
||||
|
||||
Plan: 0 to add, 0 to change, 1 to destroy.`, output)
|
||||
|
||||
expRemotePlanArgs := []string{"plan", "-input=false", "-refresh", "-no-color", "extra", "args", "comment", "args"}
|
||||
Equals(t, expRemotePlanArgs, asyncTf.CalledArgs)
|
||||
expRemotePlanArgs := []string{"plan", "-input=false", "-refresh", "-no-color", "extra", "args", "comment", "args"}
|
||||
Equals(t, expRemotePlanArgs, asyncTf.CalledArgs)
|
||||
|
||||
// Verify that the fake plan file we write has the correct contents.
|
||||
bytes, err := ioutil.ReadFile(filepath.Join(absProjectPath, "default.tfplan"))
|
||||
Ok(t, err)
|
||||
Equals(t, `Atlantis: this plan was created by remote ops
|
||||
// Verify that the fake plan file we write has the correct contents.
|
||||
bytes, err := ioutil.ReadFile(filepath.Join(absProjectPath, "default.tfplan"))
|
||||
Ok(t, err)
|
||||
Equals(t, `Atlantis: this plan was created by remote ops
|
||||
|
||||
An execution plan has been generated and is shown below.
|
||||
Resource actions are indicated with the following symbols:
|
||||
@@ -765,10 +776,12 @@ Terraform will perform the following actions:
|
||||
|
||||
Plan: 0 to add, 0 to change, 1 to destroy.`, string(bytes))
|
||||
|
||||
// Ensure that the status was updated with the runURL.
|
||||
runURL := "https://app.terraform.io/app/lkysow-enterprises/atlantis-tfe-test/runs/run-is4oVvJfrkud1KvE"
|
||||
updater.VerifyWasCalledOnce().UpdateProject(ctx, models.PlanCommand, models.PendingCommitStatus, runURL)
|
||||
updater.VerifyWasCalledOnce().UpdateProject(ctx, models.PlanCommand, models.SuccessCommitStatus, runURL)
|
||||
// Ensure that the status was updated with the runURL.
|
||||
runURL := "https://app.terraform.io/app/lkysow-enterprises/atlantis-tfe-test/runs/run-is4oVvJfrkud1KvE"
|
||||
updater.VerifyWasCalledOnce().UpdateProject(ctx, models.PlanCommand, models.PendingCommitStatus, runURL)
|
||||
updater.VerifyWasCalledOnce().UpdateProject(ctx, models.PlanCommand, models.SuccessCommitStatus, runURL)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type remotePlanMock struct {
|
||||
|
||||
Reference in New Issue
Block a user