Merge pull request #30858 from hashicorp/sebasslash/err-approval-input-false

Handle -input=false in cloud integration
This commit is contained in:
Sebastian Rivera 2022-04-26 14:25:33 -04:00 committed by GitHub
commit 8089f090f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 84 additions and 11 deletions

View File

@ -44,7 +44,8 @@ BUG FIXES:
* When rendering a diff, Terraform now quotes the name of any object attribute whose string representation is not a valid identifier. ([#30766](https://github.com/hashicorp/terraform/issues/30766))
* Terraform will prioritize local terraform variables over remote terraform variables in operations such as `import`, `plan`, `refresh` and `apply` for workspaces in local execution mode. This behavior applies to both `remote` backend and the `cloud` integration configuration. ([#29972](https://github.com/hashicorp/terraform/issues/29972))
* `terraform show -json`: JSON plan output now correctly maps aliased providers to their configurations, and includes the full provider source address alongside the short provider name. ([#30138](https://github.com/hashicorp/terraform/issues/30138))
* The local token configuration now has higher priority than the token specified in a CLI configuration. ([#30664](https://github.com/hashicorp/terraform/issues/30664))
* The local token configuration in the `cloud` and `remote` backend now has higher priority than the token specified in a CLI configuration. ([#30664](https://github.com/hashicorp/terraform/issues/30664))
* The `cloud` integration now gracefully exits when `-input=false` and an operation requires some user input.
## Previous Releases

View File

@ -89,6 +89,10 @@ type Cloud struct {
ignoreVersionConflict bool
runningInAutomation bool
// input stores the value of the -input flag, since it will be used
// to determine whether or not to ask the user for approval of a run.
input bool
}
var _ backend.Backend = (*Cloud)(nil)

View File

@ -100,7 +100,7 @@ func (b *Cloud) opApply(stopCtx, cancelCtx context.Context, op *backend.Operatio
mustConfirm := (op.UIIn != nil && op.UIOut != nil) && !op.AutoApprove
if mustConfirm {
if mustConfirm && b.input {
opts := &terraform.InputOpts{Id: "approve"}
if op.PlanMode == plans.DestroyMode {
@ -117,6 +117,8 @@ func (b *Cloud) opApply(stopCtx, cancelCtx context.Context, op *backend.Operatio
if err != nil && err != errRunApproved {
return r, err
}
} else if mustConfirm && !b.input {
return r, errApplyNeedsUIConfirmation
} else {
// If we don't need to ask for confirmation, insert a blank
// line to separate the ouputs.

View File

@ -16,6 +16,7 @@ func (b *Cloud) CLIInit(opts *backend.CLIOpts) error {
b.CLIColor = opts.CLIColor
b.ContextOpts = opts.ContextOpts
b.runningInAutomation = opts.RunningInAutomation
b.input = opts.Input
return nil
}

View File

@ -3,7 +3,6 @@ package cloud
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"math"
@ -17,14 +16,6 @@ import (
"github.com/hashicorp/terraform/internal/terraform"
)
var (
errApplyDiscarded = errors.New("Apply discarded.")
errDestroyDiscarded = errors.New("Destroy discarded.")
errRunApproved = errors.New("approved using the UI or API")
errRunDiscarded = errors.New("discarded using the UI or API")
errRunOverridden = errors.New("overridden using the UI or API")
)
var (
backoffMin = 1000.0
backoffMax = 3000.0
@ -388,6 +379,8 @@ func (b *Cloud) checkPolicy(stopCtx, cancelCtx context.Context, op *backend.Oper
if _, err = b.client.PolicyChecks.Override(stopCtx, pc.ID); err != nil {
return generalError(fmt.Sprintf("Failed to override policy check.\n%s", runUrl), err)
}
} else if !b.input {
return errPolicyOverrideNeedsUIConfirmation
} else {
opts := &terraform.InputOpts{
Id: "override",

View File

@ -0,0 +1,58 @@
package main
import (
"testing"
)
func Test_apply_no_input_flag(t *testing.T) {
t.Parallel()
skipIfMissingEnvVar(t)
cases := testCases{
"terraform apply with -input=false": {
operations: []operationSets{
{
prep: func(t *testing.T, orgName, dir string) {
wsName := "new-workspace"
tfBlock := terraformConfigCloudBackendName(orgName, wsName)
writeMainTF(t, tfBlock, dir)
},
commands: []tfCommand{
{
command: []string{"init", "-input=false"},
expectedCmdOutput: `Terraform Cloud has been successfully initialized`,
},
{
command: []string{"apply", "-input=false"},
expectedCmdOutput: `Cannot confirm apply due to -input=false. Please handle run confirmation in the UI.`,
expectError: true,
},
},
},
},
},
"terraform apply with auto approve and -input=false": {
operations: []operationSets{
{
prep: func(t *testing.T, orgName, dir string) {
wsName := "cloud-workspace"
tfBlock := terraformConfigCloudBackendName(orgName, wsName)
writeMainTF(t, tfBlock, dir)
},
commands: []tfCommand{
{
command: []string{"init", "-input=false"},
expectedCmdOutput: `Terraform Cloud has been successfully initialized`,
},
{
command: []string{"apply", "-auto-approve", "-input=false"},
expectedCmdOutput: `Apply complete!`,
},
},
},
},
},
}
testRunner(t, cases, 1)
}

View File

@ -1,6 +1,7 @@
package cloud
import (
"errors"
"fmt"
"strings"
@ -8,6 +9,18 @@ import (
"github.com/zclconf/go-cty/cty"
)
// String based errors
var (
errApplyDiscarded = errors.New("Apply discarded.")
errDestroyDiscarded = errors.New("Destroy discarded.")
errRunApproved = errors.New("approved using the UI or API")
errRunDiscarded = errors.New("discarded using the UI or API")
errRunOverridden = errors.New("overridden using the UI or API")
errApplyNeedsUIConfirmation = errors.New("Cannot confirm apply due to -input=false. Please handle run confirmation in the UI.")
errPolicyOverrideNeedsUIConfirmation = errors.New("Cannot override soft failed policy checks when -input=false. Please open the run in the UI to override.")
)
// Diagnostic error messages
var (
invalidWorkspaceConfigMissingValues = tfdiags.AttributeValue(
tfdiags.Error,

View File

@ -154,6 +154,7 @@ func testBackend(t *testing.T, obj cty.Value) (*Cloud, func()) {
// Set local to a local test backend.
b.local = testLocalBackend(t, b)
b.input = true
ctx := context.Background()