move backend operation cancellation into meta

Create a single command method for running and operation with
cancellation.
This commit is contained in:
James Bardin 2018-02-09 18:51:29 -05:00
parent 7cba68326a
commit 67a6152091
4 changed files with 55 additions and 122 deletions

View File

@ -2,12 +2,10 @@ package command
import (
"bytes"
"context"
"fmt"
"os"
"sort"
"strings"
"time"
"github.com/hashicorp/terraform/tfdiags"
@ -159,47 +157,9 @@ func (c *ApplyCommand) Run(args []string) int {
opReq.AutoApprove = autoApprove
opReq.DestroyForce = destroyForce
// Perform the operation
op, err := b.Operation(context.Background(), opReq)
op, err := c.RunOperation(b, opReq)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error starting operation: %s", err))
return 1
}
// Wait for the operation to complete or an interrupt to occur
select {
case <-c.ShutdownCh:
// gracefully stop the operation
op.Stop()
// Notify the user
c.Ui.Output(outputInterrupt)
// Still get the result, since there is still one
select {
case <-c.ShutdownCh:
c.Ui.Error(
"Two interrupts received. Exiting immediately. Note that data\n" +
"loss may have occurred.")
// cancel the operation completely
op.Cancel()
// the operation should return asap
// but timeout just in case
select {
case <-op.Done():
case <-time.After(5 * time.Second):
}
return 1
case <-op.Done():
}
case <-op.Done():
if err := op.Err; err != nil {
diags = diags.Append(err)
}
diags = diags.Append(err)
}
c.showDiagnostics(diags)

View File

@ -3,6 +3,7 @@ package command
import (
"bufio"
"bytes"
"context"
"errors"
"flag"
"fmt"
@ -259,6 +260,54 @@ func (m *Meta) StdinPiped() bool {
return fi.Mode()&os.ModeNamedPipe != 0
}
func (m *Meta) RunOperation(b backend.Enhanced, opReq *backend.Operation) (*backend.RunningOperation, error) {
op, err := b.Operation(context.Background(), opReq)
if err != nil {
return nil, fmt.Errorf("error starting operation: %s", err)
}
// Wait for the operation to complete or an interrupt to occur
select {
case <-m.ShutdownCh:
// gracefully stop the operation
op.Stop()
// Notify the user
m.Ui.Output(outputInterrupt)
// Still get the result, since there is still one
select {
case <-m.ShutdownCh:
m.Ui.Error(
"Two interrupts received. Exiting immediately. Note that data\n" +
"loss may have occurred.")
// cancel the operation completely
op.Cancel()
// the operation should return asap
// but timeout just in case
select {
case <-op.Done():
case <-time.After(5 * time.Second):
}
return nil, errors.New("operation canceled")
case <-op.Done():
// operation completed after Stop
}
case <-op.Done():
// operation completed normally
}
if op.Err != nil {
return op, op.Err
}
return op, nil
}
const (
ProviderSkipVerifyEnvVar = "TF_SKIP_PROVIDER_VERIFY"
)

View File

@ -1,10 +1,8 @@
package command
import (
"context"
"fmt"
"strings"
"time"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/config"
@ -108,43 +106,9 @@ func (c *PlanCommand) Run(args []string) int {
opReq.Type = backend.OperationTypePlan
// Perform the operation
op, err := b.Operation(context.Background(), opReq)
op, err := c.RunOperation(b, opReq)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error starting operation: %s", err))
return 1
}
select {
case <-c.ShutdownCh:
// Cancel our context so we can start gracefully exiting
op.Stop()
// Notify the user
c.Ui.Output(outputInterrupt)
// Still get the result, since there is still one
select {
case <-c.ShutdownCh:
c.Ui.Error(
"Two interrupts received. Exiting immediately")
// cancel the operation completely
op.Cancel()
// the operation should return asap
// but timeout just in case
select {
case <-op.Done():
case <-time.After(5 * time.Second):
}
return 1
case <-op.Done():
}
case <-op.Done():
if err := op.Err; err != nil {
diags = diags.Append(err)
}
diags = diags.Append(err)
}
c.showDiagnostics(diags)

View File

@ -1,10 +1,8 @@
package command
import (
"context"
"fmt"
"strings"
"time"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/config"
@ -76,47 +74,9 @@ func (c *RefreshCommand) Run(args []string) int {
opReq.Type = backend.OperationTypeRefresh
opReq.Module = mod
// Perform the operation
op, err := b.Operation(context.Background(), opReq)
op, err := c.RunOperation(b, opReq)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error starting operation: %s", err))
return 1
}
// Wait for the operation to complete or an interrupt to occur
select {
case <-c.ShutdownCh:
// gracefully stop the operation
op.Stop()
// Notify the user
c.Ui.Output(outputInterrupt)
// Still get the result, since there is still one
select {
case <-c.ShutdownCh:
c.Ui.Error(
"Two interrupts received. Exiting immediately. Note that data\n" +
"loss may have occurred.")
// cancel the operation completely
op.Cancel()
// the operation should return asap
// but timeout just in case
select {
case <-op.Done():
case <-time.After(5 * time.Second):
}
return 1
case <-op.Done():
}
case <-op.Done():
if err := op.Err; err != nil {
diags = diags.Append(err)
}
diags = diags.Append(err)
}
c.showDiagnostics(diags)