Add checks for all flags we currently don’t support

For Plan only:
-module-depth=n

For Plan & Apply
-parallelism=m
-refresh=false
-var “foo=bar” and -var-file=foo
This commit is contained in:
Sander van Harmelen 2018-10-05 15:25:17 +02:00
parent 979aa812df
commit 67db9da000
9 changed files with 292 additions and 14 deletions

View File

@ -137,11 +137,13 @@ type Operation struct {
// The options below are more self-explanatory and affect the runtime
// behavior of the operation.
AutoApprove bool
Destroy bool
DestroyForce bool
ModuleDepth int
Parallelism int
Targets []string
Variables map[string]interface{}
AutoApprove bool
DestroyForce bool
// Input/output/control options.
UIIn terraform.UIInput

View File

@ -24,8 +24,10 @@ import (
)
const (
defaultHostname = "app.terraform.io"
serviceID = "tfe.v2"
defaultHostname = "app.terraform.io"
defaultModuleDepth = -1
defaultParallelism = 10
serviceID = "tfe.v2"
)
// Remote is an implementation of EnhancedBackend that performs all

View File

@ -31,14 +31,27 @@ func (b *Remote) opApply(stopCtx, cancelCtx context.Context, op *backend.Operati
return nil, fmt.Errorf(strings.TrimSpace(applyErrVCSNotSupported))
}
if op.Parallelism != defaultParallelism {
return nil, fmt.Errorf(strings.TrimSpace(applyErrParallelismNotSupported))
}
if op.Plan != nil {
return nil, fmt.Errorf(strings.TrimSpace(applyErrPlanNotSupported))
}
if !op.PlanRefresh {
return nil, fmt.Errorf(strings.TrimSpace(applyErrNoRefreshNotSupported))
}
if op.Targets != nil {
return nil, fmt.Errorf(strings.TrimSpace(applyErrTargetsNotSupported))
}
if op.Variables != nil {
return nil, fmt.Errorf(strings.TrimSpace(
fmt.Sprintf(applyErrVariablesNotSupported, b.hostname, b.organization, op.Workspace)))
}
if (op.Module == nil || op.Module.Config().Dir == "") && !op.Destroy {
return nil, fmt.Errorf(strings.TrimSpace(applyErrNoConfig))
}
@ -96,7 +109,7 @@ func (b *Remote) opApply(stopCtx, cancelCtx context.Context, op *backend.Operati
}
}
return r, fmt.Errorf(strings.TrimSpace(
fmt.Sprint(applyErrNoApplyRights, b.hostname, b.organization, op.Workspace)))
fmt.Sprintf(applyErrNoApplyRights, b.hostname, b.organization, op.Workspace)))
}
hasUI := op.UIIn != nil && op.UIOut != nil
@ -286,11 +299,25 @@ A workspace that is connected to a VCS requires the VCS-driven workflow
to ensure that the VCS remains the single source of truth.
`
const applyErrParallelismNotSupported = `
Custom parallelism values are currently not supported!
The "remote" backend does not support setting a custom parallelism
value at this time.
`
const applyErrPlanNotSupported = `
Applying a saved plan is currently not supported!
The "remote" backend currently requires configuration to be present
and does not accept an existing saved plan as an argument at this time.
The "remote" backend currently requires configuration to be present and
does not accept an existing saved plan as an argument at this time.
`
const applyErrNoRefreshNotSupported = `
Applying without refresh is currently not supported!
Currently the "remote" backend will always do an in-memory refresh of
the Terraform state prior to generating the plan.
`
const applyErrTargetsNotSupported = `
@ -299,6 +326,19 @@ Resource targeting is currently not supported!
The "remote" backend does not support resource targeting at this time.
`
const applyErrVariablesNotSupported = `
Run variables are currently not supported!
The "remote" backend does not support setting run variables at this time.
Currently the only to way to pass variables to the remote backend is by
creating a '*.auto.tfvars' variables file. This file will automatically
be loaded by the "remote" backend when the workspace is configured to use
Terraform v0.10.0 or later.
Additionally you can also set variables on the workspace in the web UI:
https://%s/app/%s/%s/variables
`
const applyErrNoConfig = `
No configuration files found!

View File

@ -18,7 +18,9 @@ import (
func testOperationApply() *backend.Operation {
return &backend.Operation{
Type: backend.OperationTypeApply,
Parallelism: defaultParallelism,
PlanRefresh: true,
Type: backend.OperationTypeApply,
}
}
@ -135,6 +137,31 @@ func TestRemote_applyWithVCS(t *testing.T) {
}
}
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)
}
<-run.Done()
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)
}
}
func TestRemote_applyWithPlan(t *testing.T) {
b := testBackendDefault(t)
@ -160,6 +187,31 @@ func TestRemote_applyWithPlan(t *testing.T) {
}
}
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)
}
<-run.Done()
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)
}
}
func TestRemote_applyWithTarget(t *testing.T) {
b := testBackendDefault(t)
@ -185,6 +237,31 @@ func TestRemote_applyWithTarget(t *testing.T) {
}
}
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)
}
<-run.Done()
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)
}
}
func TestRemote_applyNoConfig(t *testing.T) {
b := testBackendDefault(t)

View File

@ -30,6 +30,14 @@ func (b *Remote) opPlan(stopCtx, cancelCtx context.Context, op *backend.Operatio
return nil, fmt.Errorf(strings.TrimSpace(fmt.Sprintf(planErrNoQueueRunRights)))
}
if op.ModuleDepth != defaultModuleDepth {
return nil, fmt.Errorf(strings.TrimSpace(planErrModuleDepthNotSupported))
}
if op.Parallelism != defaultParallelism {
return nil, fmt.Errorf(strings.TrimSpace(planErrParallelismNotSupported))
}
if op.Plan != nil {
return nil, fmt.Errorf(strings.TrimSpace(planErrPlanNotSupported))
}
@ -38,10 +46,19 @@ func (b *Remote) opPlan(stopCtx, cancelCtx context.Context, op *backend.Operatio
return nil, fmt.Errorf(strings.TrimSpace(planErrOutPathNotSupported))
}
if !op.PlanRefresh {
return nil, fmt.Errorf(strings.TrimSpace(planErrNoRefreshNotSupported))
}
if op.Targets != nil {
return nil, fmt.Errorf(strings.TrimSpace(planErrTargetsNotSupported))
}
if op.Variables != nil {
return nil, fmt.Errorf(strings.TrimSpace(
fmt.Sprintf(planErrVariablesNotSupported, b.hostname, b.organization, op.Workspace)))
}
if (op.Module == nil || op.Module.Config().Dir == "") && !op.Destroy {
return nil, fmt.Errorf(strings.TrimSpace(planErrNoConfig))
}
@ -203,11 +220,25 @@ Insufficient rights to generate a plan!
to generate plans, at least plan permissions on the workspace are required.[reset]
`
const planErrModuleDepthNotSupported = `
Custom module depths are currently not supported!
The "remote" backend does not support setting a custom module
depth at this time.
`
const planErrParallelismNotSupported = `
Custom parallelism values are currently not supported!
The "remote" backend does not support setting a custom parallelism
value at this time.
`
const planErrPlanNotSupported = `
Displaying a saved plan is currently not supported!
The "remote" backend currently requires configuration to be present
and does not accept an existing saved plan as an argument at this time.
The "remote" backend currently requires configuration to be present and
does not accept an existing saved plan as an argument at this time.
`
const planErrOutPathNotSupported = `
@ -217,12 +248,32 @@ The "remote" backend does not support saving the generated execution
plan locally at this time.
`
const planErrNoRefreshNotSupported = `
Planning without refresh is currently not supported!
Currently the "remote" backend will always do an in-memory refresh of
the Terraform state prior to generating the plan.
`
const planErrTargetsNotSupported = `
Resource targeting is currently not supported!
The "remote" backend does not support resource targeting at this time.
`
const planErrVariablesNotSupported = `
Run variables are currently not supported!
The "remote" backend does not support setting run variables at this time.
Currently the only to way to pass variables to the remote backend is by
creating a '*.auto.tfvars' variables file. This file will automatically
be loaded by the "remote" backend when the workspace is configured to use
Terraform v0.10.0 or later.
Additionally you can also set variables on the workspace in the web UI:
https://%s/app/%s/%s/variables
`
const planErrNoConfig = `
No configuration files found!

View File

@ -18,7 +18,10 @@ import (
func testOperationPlan() *backend.Operation {
return &backend.Operation{
Type: backend.OperationTypePlan,
ModuleDepth: defaultModuleDepth,
Parallelism: defaultParallelism,
PlanRefresh: true,
Type: backend.OperationTypePlan,
}
}
@ -85,6 +88,56 @@ func TestRemote_planWithoutPermissions(t *testing.T) {
}
}
func TestRemote_planWithModuleDepth(t *testing.T) {
b := testBackendDefault(t)
mod, modCleanup := module.TestTree(t, "./test-fixtures/plan")
defer modCleanup()
op := testOperationPlan()
op.Module = mod
op.ModuleDepth = 1
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 a plan error, got: %v", run.Err)
}
if !strings.Contains(run.Err.Error(), "module depths are currently not supported") {
t.Fatalf("expected a module depth error, got: %v", run.Err)
}
}
func TestRemote_planWithParallelism(t *testing.T) {
b := testBackendDefault(t)
mod, modCleanup := module.TestTree(t, "./test-fixtures/plan")
defer modCleanup()
op := testOperationPlan()
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)
}
<-run.Done()
if run.Err == nil {
t.Fatalf("expected a plan 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)
}
}
func TestRemote_planWithPlan(t *testing.T) {
b := testBackendDefault(t)
@ -135,6 +188,31 @@ func TestRemote_planWithPath(t *testing.T) {
}
}
func TestRemote_planWithoutRefresh(t *testing.T) {
b := testBackendDefault(t)
mod, modCleanup := module.TestTree(t, "./test-fixtures/plan")
defer modCleanup()
op := testOperationPlan()
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)
}
<-run.Done()
if run.Err == nil {
t.Fatalf("expected a plan 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)
}
}
func TestRemote_planWithTarget(t *testing.T) {
b := testBackendDefault(t)
@ -160,6 +238,31 @@ func TestRemote_planWithTarget(t *testing.T) {
}
}
func TestRemote_planWithVariables(t *testing.T) {
b := testBackendDefault(t)
mod, modCleanup := module.TestTree(t, "./test-fixtures/plan")
defer modCleanup()
op := testOperationPlan()
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)
}
<-run.Done()
if run.Err == nil {
t.Fatalf("expected an plan 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)
}
}
func TestRemote_planNoConfig(t *testing.T) {
b := testBackendDefault(t)

View File

@ -147,13 +147,13 @@ func (c *ApplyCommand) Run(args []string) int {
// Build the operation
opReq := c.Operation()
opReq.AutoApprove = autoApprove
opReq.Destroy = c.Destroy
opReq.DestroyForce = destroyForce
opReq.Module = mod
opReq.Plan = plan
opReq.PlanRefresh = refresh
opReq.Type = backend.OperationTypeApply
opReq.AutoApprove = autoApprove
opReq.DestroyForce = destroyForce
op, err := c.RunOperation(b, opReq)
if err != nil {

View File

@ -167,9 +167,11 @@ func (m *Meta) IsLocalBackend(b backend.Backend) bool {
func (m *Meta) Operation() *backend.Operation {
return &backend.Operation{
PlanOutBackend: m.backendState,
Parallelism: m.parallelism,
Targets: m.targets,
UIIn: m.UIInput(),
UIOut: m.Ui,
Variables: m.variables,
Workspace: m.Workspace(),
LockState: m.stateLock,
StateLockTimeout: m.stateLockTimeout,

View File

@ -100,9 +100,10 @@ func (c *PlanCommand) Run(args []string) int {
opReq := c.Operation()
opReq.Destroy = destroy
opReq.Module = mod
opReq.ModuleDepth = moduleDepth
opReq.Plan = plan
opReq.PlanRefresh = refresh
opReq.PlanOutPath = outPath
opReq.PlanRefresh = refresh
opReq.Type = backend.OperationTypePlan
// Perform the operation