2018-09-26 17:27:12 +02:00
|
|
|
package remote
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"strings"
|
|
|
|
"syscall"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
tfe "github.com/hashicorp/go-tfe"
|
|
|
|
"github.com/hashicorp/terraform/backend"
|
|
|
|
"github.com/hashicorp/terraform/config/module"
|
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
)
|
|
|
|
|
|
|
|
func testOperationApply() *backend.Operation {
|
|
|
|
return &backend.Operation{
|
2018-10-05 15:25:17 +02:00
|
|
|
Parallelism: defaultParallelism,
|
|
|
|
PlanRefresh: true,
|
|
|
|
Type: backend.OperationTypeApply,
|
2018-09-26 17:27:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemote_applyBasic(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"approve": "yes",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("error running operation: %v", run.Err)
|
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if run.PlanEmpty {
|
|
|
|
t.Fatalf("expected a non-empty plan")
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
|
|
|
|
if len(input.answers) > 0 {
|
|
|
|
t.Fatalf("expected no unused answers, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
|
|
|
|
t.Fatalf("missing plan summery in output: %s", output)
|
|
|
|
}
|
|
|
|
if !strings.Contains(output, "1 added, 0 changed, 0 destroyed") {
|
|
|
|
t.Fatalf("missing apply summery in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-08 12:31:21 +02:00
|
|
|
func TestRemote_applyWithAutoApply(t *testing.T) {
|
|
|
|
b := testBackendNoDefault(t)
|
|
|
|
|
|
|
|
// Create a named workspace that auto applies.
|
|
|
|
_, err := b.client.Workspaces.Create(
|
|
|
|
context.Background(),
|
|
|
|
b.organization,
|
|
|
|
tfe.WorkspaceCreateOptions{
|
|
|
|
AutoApply: tfe.Bool(true),
|
|
|
|
Name: tfe.String(b.prefix + "prod"),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error creating named workspace: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"approve": "yes",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = "prod"
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("error running operation: %v", run.Err)
|
|
|
|
}
|
|
|
|
if run.PlanEmpty {
|
|
|
|
t.Fatalf("expected a non-empty plan")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(input.answers) != 1 {
|
|
|
|
t.Fatalf("expected an unused answer, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
|
|
|
|
t.Fatalf("missing plan summery in output: %s", output)
|
|
|
|
}
|
|
|
|
if !strings.Contains(output, "1 added, 0 changed, 0 destroyed") {
|
|
|
|
t.Fatalf("missing apply summery in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-04 22:53:56 +02:00
|
|
|
func TestRemote_applyWithoutPermissions(t *testing.T) {
|
|
|
|
b := testBackendNoDefault(t)
|
|
|
|
|
|
|
|
// Create a named workspace without permissions.
|
|
|
|
w, err := b.client.Workspaces.Create(
|
|
|
|
context.Background(),
|
|
|
|
b.organization,
|
|
|
|
tfe.WorkspaceCreateOptions{
|
|
|
|
Name: tfe.String(b.prefix + "prod"),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error creating named workspace: %v", err)
|
|
|
|
}
|
|
|
|
w.Permissions.CanUpdate = false
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.Workspace = "prod"
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-08 12:31:21 +02:00
|
|
|
<-run.Done()
|
2018-10-04 22:53:56 +02:00
|
|
|
if run.Err == nil {
|
|
|
|
t.Fatalf("expected an apply error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(run.Err.Error(), "insufficient rights to apply changes") {
|
|
|
|
t.Fatalf("expected a permissions error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 17:27:12 +02:00
|
|
|
func TestRemote_applyWithVCS(t *testing.T) {
|
|
|
|
b := testBackendNoDefault(t)
|
|
|
|
|
2018-10-04 22:53:56 +02:00
|
|
|
// Create a named workspace with a VCS.
|
2018-09-26 17:27:12 +02:00
|
|
|
_, err := b.client.Workspaces.Create(
|
|
|
|
context.Background(),
|
|
|
|
b.organization,
|
|
|
|
tfe.WorkspaceCreateOptions{
|
|
|
|
Name: tfe.String(b.prefix + "prod"),
|
|
|
|
VCSRepo: &tfe.VCSRepoOptions{},
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error creating named workspace: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.Workspace = "prod"
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-08 12:31:21 +02:00
|
|
|
<-run.Done()
|
2018-09-26 17:27:12 +02:00
|
|
|
if run.Err == nil {
|
2018-09-26 21:35:26 +02:00
|
|
|
t.Fatalf("expected an apply error, got: %v", run.Err)
|
2018-09-26 17:27:12 +02:00
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if !run.PlanEmpty {
|
|
|
|
t.Fatalf("expected plan to be empty")
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
if !strings.Contains(run.Err.Error(), "not allowed for workspaces with a VCS") {
|
|
|
|
t.Fatalf("expected a VCS error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 15:25:17 +02:00
|
|
|
func TestRemote_applyWithParallelism(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.Parallelism = 3
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-08 12:31:21 +02:00
|
|
|
<-run.Done()
|
2018-10-05 15:25:17 +02:00
|
|
|
if run.Err == nil {
|
|
|
|
t.Fatalf("expected an apply error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(run.Err.Error(), "parallelism values are currently not supported") {
|
|
|
|
t.Fatalf("expected a parallelism error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 17:27:12 +02:00
|
|
|
func TestRemote_applyWithPlan(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.Plan = &terraform.Plan{}
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-08 12:31:21 +02:00
|
|
|
<-run.Done()
|
2018-09-26 17:27:12 +02:00
|
|
|
if run.Err == nil {
|
2018-09-26 21:35:26 +02:00
|
|
|
t.Fatalf("expected an apply error, got: %v", run.Err)
|
2018-09-26 17:27:12 +02:00
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if !run.PlanEmpty {
|
|
|
|
t.Fatalf("expected plan to be empty")
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
if !strings.Contains(run.Err.Error(), "saved plan is currently not supported") {
|
|
|
|
t.Fatalf("expected a saved plan error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 15:25:17 +02:00
|
|
|
func TestRemote_applyWithoutRefresh(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.PlanRefresh = false
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-08 12:31:21 +02:00
|
|
|
<-run.Done()
|
2018-10-05 15:25:17 +02:00
|
|
|
if run.Err == nil {
|
|
|
|
t.Fatalf("expected an apply error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(run.Err.Error(), "refresh is currently not supported") {
|
|
|
|
t.Fatalf("expected a refresh error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 17:27:12 +02:00
|
|
|
func TestRemote_applyWithTarget(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.Targets = []string{"null_resource.foo"}
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-08 12:31:21 +02:00
|
|
|
<-run.Done()
|
2018-09-26 17:27:12 +02:00
|
|
|
if run.Err == nil {
|
2018-09-26 21:35:26 +02:00
|
|
|
t.Fatalf("expected an apply error, got: %v", run.Err)
|
2018-09-26 17:27:12 +02:00
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if !run.PlanEmpty {
|
|
|
|
t.Fatalf("expected plan to be empty")
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
if !strings.Contains(run.Err.Error(), "targeting is currently not supported") {
|
|
|
|
t.Fatalf("expected a targeting error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
}
|
2018-10-05 15:25:17 +02:00
|
|
|
|
|
|
|
func TestRemote_applyWithVariables(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.Variables = map[string]interface{}{"foo": "bar"}
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-08 12:31:21 +02:00
|
|
|
<-run.Done()
|
2018-10-05 15:25:17 +02:00
|
|
|
if run.Err == nil {
|
|
|
|
t.Fatalf("expected an apply error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(run.Err.Error(), "variables are currently not supported") {
|
|
|
|
t.Fatalf("expected a variables error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
|
|
|
|
func TestRemote_applyNoConfig(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = nil
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-08 12:31:21 +02:00
|
|
|
<-run.Done()
|
2018-09-26 17:27:12 +02:00
|
|
|
if run.Err == nil {
|
2018-09-26 21:35:26 +02:00
|
|
|
t.Fatalf("expected an apply error, got: %v", run.Err)
|
2018-09-26 17:27:12 +02:00
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if !run.PlanEmpty {
|
|
|
|
t.Fatalf("expected plan to be empty")
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
if !strings.Contains(run.Err.Error(), "configuration files found") {
|
|
|
|
t.Fatalf("expected configuration files error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemote_applyNoChanges(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply-no-changes")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("error running operation: %v", run.Err)
|
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if !run.PlanEmpty {
|
|
|
|
t.Fatalf("expected plan to be empty")
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "No changes. Infrastructure is up-to-date.") {
|
|
|
|
t.Fatalf("expected no changes in plan summery: %s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemote_applyNoApprove(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"approve": "no",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err == nil {
|
2018-09-26 21:35:26 +02:00
|
|
|
t.Fatalf("expected an apply error, got: %v", run.Err)
|
2018-09-26 17:27:12 +02:00
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if !run.PlanEmpty {
|
|
|
|
t.Fatalf("expected plan to be empty")
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
if !strings.Contains(run.Err.Error(), "Apply discarded") {
|
2018-09-26 21:35:26 +02:00
|
|
|
t.Fatalf("expected an apply discarded error, got: %v", run.Err)
|
2018-09-26 17:27:12 +02:00
|
|
|
}
|
|
|
|
if len(input.answers) > 0 {
|
|
|
|
t.Fatalf("expected no unused answers, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemote_applyAutoApprove(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"approve": "no",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.AutoApprove = true
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("error running operation: %v", run.Err)
|
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if run.PlanEmpty {
|
|
|
|
t.Fatalf("expected a non-empty plan")
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
|
|
|
|
if len(input.answers) != 1 {
|
|
|
|
t.Fatalf("expected an unused answer, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
|
|
|
|
t.Fatalf("missing plan summery in output: %s", output)
|
|
|
|
}
|
|
|
|
if !strings.Contains(output, "1 added, 0 changed, 0 destroyed") {
|
|
|
|
t.Fatalf("missing apply summery in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemote_applyLockTimeout(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
// Retrieve the workspace used to run this operation in.
|
|
|
|
w, err := b.client.Workspaces.Read(ctx, b.organization, b.workspace)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error retrieving workspace: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new configuration version.
|
|
|
|
c, err := b.client.ConfigurationVersions.Create(ctx, w.ID, tfe.ConfigurationVersionCreateOptions{})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error creating configuration version: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a pending run to block this run.
|
|
|
|
_, err = b.client.Runs.Create(ctx, tfe.RunCreateOptions{
|
|
|
|
ConfigurationVersion: c,
|
|
|
|
Workspace: w,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error creating pending run: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
2018-10-01 10:18:59 +02:00
|
|
|
"cancel": "yes",
|
2018-09-26 17:27:12 +02:00
|
|
|
"approve": "yes",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.StateLockTimeout = 5 * time.Second
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
_, err = b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
sigint := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigint, syscall.SIGINT)
|
|
|
|
select {
|
|
|
|
case <-sigint:
|
|
|
|
// Stop redirecting SIGINT signals.
|
|
|
|
signal.Stop(sigint)
|
|
|
|
case <-time.After(10 * time.Second):
|
|
|
|
t.Fatalf("expected lock timeout after 5 seconds, waited 10 seconds")
|
|
|
|
}
|
|
|
|
|
2018-10-01 10:18:59 +02:00
|
|
|
if len(input.answers) != 2 {
|
|
|
|
t.Fatalf("expected unused answers, got: %v", input.answers)
|
2018-09-26 17:27:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "Lock timeout exceeded") {
|
|
|
|
t.Fatalf("missing lock timout error in output: %s", output)
|
|
|
|
}
|
|
|
|
if strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
|
|
|
|
t.Fatalf("unexpected plan summery in output: %s", output)
|
|
|
|
}
|
|
|
|
if strings.Contains(output, "1 added, 0 changed, 0 destroyed") {
|
|
|
|
t.Fatalf("unexpected apply summery in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemote_applyDestroy(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply-destroy")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"approve": "yes",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Destroy = true
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("error running operation: %v", run.Err)
|
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if run.PlanEmpty {
|
|
|
|
t.Fatalf("expected a non-empty plan")
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
|
|
|
|
if len(input.answers) > 0 {
|
|
|
|
t.Fatalf("expected no unused answers, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "0 to add, 0 to change, 1 to destroy") {
|
|
|
|
t.Fatalf("missing plan summery in output: %s", output)
|
|
|
|
}
|
|
|
|
if !strings.Contains(output, "0 added, 0 changed, 1 destroyed") {
|
|
|
|
t.Fatalf("missing apply summery in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemote_applyDestroyNoConfig(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"approve": "yes",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Destroy = true
|
|
|
|
op.Module = nil
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("unexpected apply error: %v", run.Err)
|
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if run.PlanEmpty {
|
|
|
|
t.Fatalf("expected a non-empty plan")
|
|
|
|
}
|
2018-09-26 17:27:12 +02:00
|
|
|
|
|
|
|
if len(input.answers) > 0 {
|
|
|
|
t.Fatalf("expected no unused answers, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
}
|
2018-09-26 21:35:26 +02:00
|
|
|
|
|
|
|
func TestRemote_applyPolicyPass(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply-policy-passed")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"approve": "yes",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("error running operation: %v", run.Err)
|
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if run.PlanEmpty {
|
|
|
|
t.Fatalf("expected a non-empty plan")
|
|
|
|
}
|
2018-09-26 21:35:26 +02:00
|
|
|
|
|
|
|
if len(input.answers) > 0 {
|
|
|
|
t.Fatalf("expected no unused answers, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
|
|
|
|
t.Fatalf("missing plan summery in output: %s", output)
|
|
|
|
}
|
2018-10-04 23:03:40 +02:00
|
|
|
if !strings.Contains(output, "Sentinel Result: true") {
|
2018-10-01 12:00:23 +02:00
|
|
|
t.Fatalf("missing polic check result in output: %s", output)
|
2018-09-26 21:35:26 +02:00
|
|
|
}
|
|
|
|
if !strings.Contains(output, "1 added, 0 changed, 0 destroyed") {
|
|
|
|
t.Fatalf("missing apply summery in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemote_applyPolicyHardFail(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply-policy-hard-failed")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"approve": "yes",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err == nil {
|
|
|
|
t.Fatalf("expected an apply error, got: %v", run.Err)
|
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if !run.PlanEmpty {
|
|
|
|
t.Fatalf("expected plan to be empty")
|
|
|
|
}
|
2018-09-26 21:35:26 +02:00
|
|
|
if !strings.Contains(run.Err.Error(), "hard failed") {
|
|
|
|
t.Fatalf("expected a policy check error, got: %v", run.Err)
|
|
|
|
}
|
|
|
|
if len(input.answers) != 1 {
|
|
|
|
t.Fatalf("expected an unused answers, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
|
|
|
|
t.Fatalf("missing plan summery in output: %s", output)
|
|
|
|
}
|
|
|
|
if !strings.Contains(output, "Sentinel Result: false") {
|
2018-10-01 12:00:23 +02:00
|
|
|
t.Fatalf("missing policy check result in output: %s", output)
|
2018-09-26 21:35:26 +02:00
|
|
|
}
|
|
|
|
if strings.Contains(output, "1 added, 0 changed, 0 destroyed") {
|
|
|
|
t.Fatalf("unexpected apply summery in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemote_applyPolicySoftFail(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply-policy-soft-failed")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"override": "override",
|
|
|
|
"approve": "yes",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("error running operation: %v", run.Err)
|
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if run.PlanEmpty {
|
|
|
|
t.Fatalf("expected a non-empty plan")
|
|
|
|
}
|
2018-09-26 21:35:26 +02:00
|
|
|
|
|
|
|
if len(input.answers) > 0 {
|
|
|
|
t.Fatalf("expected no unused answers, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
|
|
|
|
t.Fatalf("missing plan summery in output: %s", output)
|
|
|
|
}
|
|
|
|
if !strings.Contains(output, "Sentinel Result: false") {
|
2018-10-01 12:00:23 +02:00
|
|
|
t.Fatalf("missing policy check result in output: %s", output)
|
2018-09-26 21:35:26 +02:00
|
|
|
}
|
|
|
|
if !strings.Contains(output, "1 added, 0 changed, 0 destroyed") {
|
|
|
|
t.Fatalf("missing apply summery in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-08 12:31:21 +02:00
|
|
|
func TestRemote_applyPolicySoftFailAutoApply(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
// Create a named workspace that auto applies.
|
|
|
|
_, err := b.client.Workspaces.Create(
|
|
|
|
context.Background(),
|
|
|
|
b.organization,
|
|
|
|
tfe.WorkspaceCreateOptions{
|
|
|
|
AutoApply: tfe.Bool(true),
|
|
|
|
Name: tfe.String(b.prefix + "prod"),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error creating named workspace: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply-policy-soft-failed")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"override": "override",
|
|
|
|
"approve": "yes",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = "prod"
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("error running operation: %v", run.Err)
|
|
|
|
}
|
|
|
|
if run.PlanEmpty {
|
|
|
|
t.Fatalf("expected a non-empty plan")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(input.answers) != 1 {
|
|
|
|
t.Fatalf("expected an unused answer, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
|
|
|
|
t.Fatalf("missing plan summery in output: %s", output)
|
|
|
|
}
|
|
|
|
if !strings.Contains(output, "Sentinel Result: false") {
|
|
|
|
t.Fatalf("missing policy check result in output: %s", output)
|
|
|
|
}
|
|
|
|
if !strings.Contains(output, "1 added, 0 changed, 0 destroyed") {
|
2018-09-26 21:35:26 +02:00
|
|
|
t.Fatalf("missing apply summery in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemote_applyPolicySoftFailAutoApprove(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply-policy-soft-failed")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
input := testInput(t, map[string]string{
|
|
|
|
"override": "override",
|
|
|
|
})
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.AutoApprove = true
|
|
|
|
op.Module = mod
|
|
|
|
op.UIIn = input
|
|
|
|
op.UIOut = b.CLI
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("error running operation: %v", run.Err)
|
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
if run.PlanEmpty {
|
|
|
|
t.Fatalf("expected a non-empty plan")
|
|
|
|
}
|
2018-09-26 21:35:26 +02:00
|
|
|
|
|
|
|
if len(input.answers) > 0 {
|
|
|
|
t.Fatalf("expected no unused answers, got: %v", input.answers)
|
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
|
|
|
|
t.Fatalf("missing plan summery in output: %s", output)
|
|
|
|
}
|
|
|
|
if !strings.Contains(output, "Sentinel Result: false") {
|
2018-10-01 12:00:23 +02:00
|
|
|
t.Fatalf("missing policy check result in output: %s", output)
|
2018-09-26 21:35:26 +02:00
|
|
|
}
|
|
|
|
if !strings.Contains(output, "1 added, 0 changed, 0 destroyed") {
|
|
|
|
t.Fatalf("missing apply summery in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|
2018-10-05 17:39:06 +02:00
|
|
|
|
|
|
|
func TestRemote_applyWithRemoteError(t *testing.T) {
|
|
|
|
b := testBackendDefault(t)
|
|
|
|
|
|
|
|
mod, modCleanup := module.TestTree(t, "./test-fixtures/apply-with-error")
|
|
|
|
defer modCleanup()
|
|
|
|
|
|
|
|
op := testOperationApply()
|
|
|
|
op.Module = mod
|
|
|
|
op.Workspace = backend.DefaultStateName
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error starting operation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-run.Done()
|
|
|
|
if run.Err != nil {
|
|
|
|
t.Fatalf("error running operation: %v", run.Err)
|
|
|
|
}
|
|
|
|
if run.ExitCode != 1 {
|
|
|
|
t.Fatalf("expected exit code 1, got %d", run.ExitCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
output := b.CLI.(*cli.MockUi).OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "null_resource.foo: 1 error") {
|
|
|
|
t.Fatalf("missing apply error in output: %s", output)
|
|
|
|
}
|
|
|
|
}
|