Merge pull request #19013 from hashicorp/f-check-errors

backend/remote: add checks for all flags we currently don’t support
This commit is contained in:
Sander van Harmelen 2018-10-05 20:37:53 +02:00 committed by GitHub
commit e0b7475984
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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 // The options below are more self-explanatory and affect the runtime
// behavior of the operation. // behavior of the operation.
AutoApprove bool
Destroy bool Destroy bool
DestroyForce bool
ModuleDepth int
Parallelism int
Targets []string Targets []string
Variables map[string]interface{} Variables map[string]interface{}
AutoApprove bool
DestroyForce bool
// Input/output/control options. // Input/output/control options.
UIIn terraform.UIInput UIIn terraform.UIInput

View File

@ -24,8 +24,10 @@ import (
) )
const ( const (
defaultHostname = "app.terraform.io" defaultHostname = "app.terraform.io"
serviceID = "tfe.v2" defaultModuleDepth = -1
defaultParallelism = 10
serviceID = "tfe.v2"
) )
// Remote is an implementation of EnhancedBackend that performs all // 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)) return nil, fmt.Errorf(strings.TrimSpace(applyErrVCSNotSupported))
} }
if op.Parallelism != defaultParallelism {
return nil, fmt.Errorf(strings.TrimSpace(applyErrParallelismNotSupported))
}
if op.Plan != nil { if op.Plan != nil {
return nil, fmt.Errorf(strings.TrimSpace(applyErrPlanNotSupported)) return nil, fmt.Errorf(strings.TrimSpace(applyErrPlanNotSupported))
} }
if !op.PlanRefresh {
return nil, fmt.Errorf(strings.TrimSpace(applyErrNoRefreshNotSupported))
}
if op.Targets != nil { if op.Targets != nil {
return nil, fmt.Errorf(strings.TrimSpace(applyErrTargetsNotSupported)) 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 { if (op.Module == nil || op.Module.Config().Dir == "") && !op.Destroy {
return nil, fmt.Errorf(strings.TrimSpace(applyErrNoConfig)) 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( 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 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. 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 = ` const applyErrPlanNotSupported = `
Applying a saved plan is currently not supported! Applying a saved plan is currently not supported!
The "remote" backend currently requires configuration to be present The "remote" backend currently requires configuration to be present and
and does not accept an existing saved plan as an argument at this time. 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 = ` const applyErrTargetsNotSupported = `
@ -299,6 +326,19 @@ Resource targeting is currently not supported!
The "remote" backend does not support resource targeting at this time. 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 = ` const applyErrNoConfig = `
No configuration files found! No configuration files found!

View File

@ -18,7 +18,9 @@ import (
func testOperationApply() *backend.Operation { func testOperationApply() *backend.Operation {
return &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) { func TestRemote_applyWithPlan(t *testing.T) {
b := testBackendDefault(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) { func TestRemote_applyWithTarget(t *testing.T) {
b := testBackendDefault(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) { func TestRemote_applyNoConfig(t *testing.T) {
b := testBackendDefault(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))) 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 { if op.Plan != nil {
return nil, fmt.Errorf(strings.TrimSpace(planErrPlanNotSupported)) 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)) return nil, fmt.Errorf(strings.TrimSpace(planErrOutPathNotSupported))
} }
if !op.PlanRefresh {
return nil, fmt.Errorf(strings.TrimSpace(planErrNoRefreshNotSupported))
}
if op.Targets != nil { if op.Targets != nil {
return nil, fmt.Errorf(strings.TrimSpace(planErrTargetsNotSupported)) 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 { if (op.Module == nil || op.Module.Config().Dir == "") && !op.Destroy {
return nil, fmt.Errorf(strings.TrimSpace(planErrNoConfig)) 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] 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 = ` const planErrPlanNotSupported = `
Displaying a saved plan is currently not supported! Displaying a saved plan is currently not supported!
The "remote" backend currently requires configuration to be present The "remote" backend currently requires configuration to be present and
and does not accept an existing saved plan as an argument at this time. does not accept an existing saved plan as an argument at this time.
` `
const planErrOutPathNotSupported = ` const planErrOutPathNotSupported = `
@ -217,12 +248,32 @@ The "remote" backend does not support saving the generated execution
plan locally at this time. 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 = ` const planErrTargetsNotSupported = `
Resource targeting is currently not supported! Resource targeting is currently not supported!
The "remote" backend does not support resource targeting at this time. 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 = ` const planErrNoConfig = `
No configuration files found! No configuration files found!

View File

@ -18,7 +18,10 @@ import (
func testOperationPlan() *backend.Operation { func testOperationPlan() *backend.Operation {
return &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) { func TestRemote_planWithPlan(t *testing.T) {
b := testBackendDefault(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) { func TestRemote_planWithTarget(t *testing.T) {
b := testBackendDefault(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) { func TestRemote_planNoConfig(t *testing.T) {
b := testBackendDefault(t) b := testBackendDefault(t)

View File

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

View File

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

View File

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