clean up operation code (#2734)

This commit is contained in:
The Magician 2018-12-26 17:42:37 -08:00 committed by Dana Hoffman
parent 4a706f7225
commit 91a513ab83
22 changed files with 487 additions and 918 deletions

View File

@ -2,12 +2,7 @@ package google
import (
"fmt"
"log"
"regexp"
"strconv"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/appengine/v1"
)
@ -18,73 +13,30 @@ var (
type AppEngineOperationWaiter struct {
Service *appengine.APIService
Op *appengine.Operation
AppId string
CommonOperationWaiter
}
func (w *AppEngineOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
matches := appEngineOperationIdRegexp.FindStringSubmatch(w.Op.Name)
if len(matches) != 2 {
return nil, "", fmt.Errorf("Expected %d results of parsing operation name, got %d from %s", 2, len(matches), w.Op.Name)
}
op, err := w.Service.Apps.Operations.Get(w.AppId, matches[1]).Do()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %v when asking for operation %q", op.Done, w.Op.Name)
return op, strconv.FormatBool(op.Done), nil
func (w *AppEngineOperationWaiter) QueryOp() (interface{}, error) {
matches := appEngineOperationIdRegexp.FindStringSubmatch(w.Op.Name)
if len(matches) != 2 {
return nil, fmt.Errorf("Expected %d results of parsing operation name, got %d from %s", 2, len(matches), w.Op.Name)
}
}
func (w *AppEngineOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"false"},
Target: []string{"true"},
Refresh: w.RefreshFunc(),
}
}
// AppEngineOperationError wraps appengine.Status and implements the
// error interface so it can be returned.
type AppEngineOperationError appengine.Status
func (e AppEngineOperationError) Error() string {
return e.Message
return w.Service.Apps.Operations.Get(w.AppId, matches[1]).Do()
}
func appEngineOperationWait(client *appengine.APIService, op *appengine.Operation, appId, activity string) error {
return appEngineOperationWaitTime(client, op, appId, activity, 4)
}
func appEngineOperationWaitTime(client *appengine.APIService, op *appengine.Operation, appId, activity string, timeoutMin int) error {
if op.Done {
if op.Error != nil {
return AppEngineOperationError(*op.Error)
}
return nil
}
func appEngineOperationWaitTime(client *appengine.APIService, op *appengine.Operation, appId, activity string, timeoutMinutes int) error {
w := &AppEngineOperationWaiter{
Service: client,
Op: op,
AppId: appId,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
if err := w.SetOp(op); err != nil {
return err
}
resultOp := opRaw.(*appengine.Operation)
if resultOp.Error != nil {
return AppEngineOperationError(*resultOp.Error)
}
return nil
return OperationWait(w, activity, timeoutMinutes)
}

View File

@ -1,77 +1,24 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/cloudfunctions/v1"
)
type CloudFunctionsOperationWaiter struct {
Service *cloudfunctions.Service
Op *cloudfunctions.Operation
CommonOperationWaiter
}
func (w *CloudFunctionsOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
op, err := w.Service.Operations.Get(w.Op.Name).Do()
if err != nil {
return nil, "", err
}
status := "PENDING"
if op.Done == true {
status = "DONE"
}
log.Printf("[DEBUG] Got %q when asking for operation %q", status, w.Op.Name)
return op, status, nil
}
func (w *CloudFunctionsOperationWaiter) QueryOp() (interface{}, error) {
return w.Service.Operations.Get(w.Op.Name).Do()
}
func (w *CloudFunctionsOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING"},
Target: []string{"DONE"},
Refresh: w.RefreshFunc(),
}
}
func cloudFunctionsOperationWait(client *cloudfunctions.Service,
op *cloudfunctions.Operation, activity string) error {
return cloudFunctionsOperationWaitTime(client, op, activity, 4)
}
func cloudFunctionsOperationWaitTime(client *cloudfunctions.Service, op *cloudfunctions.Operation,
activity string, timeoutMin int) error {
if op.Done {
if op.Error != nil {
return fmt.Errorf(op.Error.Message)
}
return nil
}
func cloudFunctionsOperationWait(service *cloudfunctions.Service, op *cloudfunctions.Operation, activity string) error {
w := &CloudFunctionsOperationWaiter{
Service: client,
Op: op,
Service: service,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
if err := w.SetOp(op); err != nil {
return err
}
resultOp := opRaw.(*cloudfunctions.Operation)
if resultOp.Error != nil {
return fmt.Errorf(resultOp.Error.Message)
}
return nil
return OperationWait(w, activity, 4)
}

143
google/common_operation.go Normal file
View File

@ -0,0 +1,143 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1"
)
type Waiter interface {
// State returns the current status of the operation.
State() string
// Error returns an error embedded in the operation we're waiting on, or nil
// if the operation has no current error.
Error() error
// SetOp sets the operation we're waiting on in a Waiter struct so that it
// can be used in other methods.
SetOp(interface{}) error
// QueryOp sends a request to the server to get the current status of the
// operation.
QueryOp() (interface{}, error)
// OpName is the name of the operation and is used to log its status.
OpName() string
// PendingStates contains the values of State() that cause us to continue
// refreshing the operation.
PendingStates() []string
// TargetStates contain the values of State() that cause us to finish
// refreshing the operation.
TargetStates() []string
}
type CommonOperationWaiter struct {
Op CommonOperation
}
func (w *CommonOperationWaiter) State() string {
return fmt.Sprintf("done: %v", w.Op.Done)
}
func (w *CommonOperationWaiter) Error() error {
if w.Op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", w.Op.Error.Code, w.Op.Error.Message)
}
return nil
}
func (w *CommonOperationWaiter) SetOp(op interface{}) error {
if err := Convert(op, &w.Op); err != nil {
return err
}
return nil
}
func (w *CommonOperationWaiter) OpName() string {
return w.Op.Name
}
func (w *CommonOperationWaiter) PendingStates() []string {
return []string{"done: false"}
}
func (w *CommonOperationWaiter) TargetStates() []string {
return []string{"done: true"}
}
func OperationDone(w Waiter) bool {
for _, s := range w.TargetStates() {
if s == w.State() {
return true
}
}
return false
}
func CommonRefreshFunc(w Waiter) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
// First, read the operation from the server.
op, err := w.QueryOp()
// If we got a non-retryable error, return it.
if err != nil && !isRetryableError(err) {
return nil, "", err
}
// Try to set the operation (so we can check it's Error/State),
// and fail if we can't.
if err = w.SetOp(op); err != nil {
return nil, "", err
}
// Fail if the operation object contains an error.
if err = w.Error(); err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %v while polling for operation %s's status", w.State(), w.OpName())
return op, w.State(), nil
}
}
func OperationWait(w Waiter, activity string, timeoutMinutes int) error {
if OperationDone(w) {
if w.Error() != nil {
return w.Error()
}
return nil
}
c := &resource.StateChangeConf{
Pending: w.PendingStates(),
Target: w.TargetStates(),
Refresh: CommonRefreshFunc(w),
Timeout: time.Duration(timeoutMinutes) * time.Minute,
MinTimeout: 2 * time.Second,
}
opRaw, err := c.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
err = w.SetOp(opRaw)
if err != nil {
return err
}
if w.Error() != nil {
return w.Error()
}
return nil
}
// The cloud resource manager API operation is an example of one of many
// interchangeable API operations. Choose it somewhat arbitrarily to represent
// the "common" operation.
type CommonOperation cloudresourcemanager.Operation

View File

@ -1,67 +1,24 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
composer "google.golang.org/api/composer/v1beta1"
)
type ComposerOperationWaiter struct {
Service *composer.ProjectsLocationsService
Op *composer.Operation
CommonOperationWaiter
}
func (w *ComposerOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
op, err := w.Service.Operations.Get(w.Op.Name).Do()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %v while polling for operation %s's 'done' status", op.Done, w.Op.Name)
return op, fmt.Sprint(op.Done), nil
}
func (w *ComposerOperationWaiter) QueryOp() (interface{}, error) {
return w.Service.Operations.Get(w.Op.Name).Do()
}
func (w *ComposerOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"false"},
Target: []string{"true"},
Refresh: w.RefreshFunc(),
}
}
func composerOperationWaitTime(service *composer.Service, op *composer.Operation, project, activity string, timeoutMin int) error {
if op.Done {
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
}
func composerOperationWaitTime(service *composer.Service, op *composer.Operation, project, activity string, timeoutMinutes int) error {
w := &ComposerOperationWaiter{
Service: service.Projects.Locations,
Op: op,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
if err := w.SetOp(op); err != nil {
return err
}
op = opRaw.(*composer.Operation)
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
return OperationWait(w, activity, timeoutMinutes)
}

View File

@ -2,11 +2,6 @@ package google
import (
"bytes"
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
@ -18,35 +13,70 @@ type ComputeOperationWaiter struct {
Project string
}
func (w *ComputeOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var op *compute.Operation
var err error
if w.Op.Zone != "" {
zone := GetResourceNameFromSelfLink(w.Op.Zone)
op, err = w.Service.ZoneOperations.Get(w.Project, zone, w.Op.Name).Do()
} else if w.Op.Region != "" {
region := GetResourceNameFromSelfLink(w.Op.Region)
op, err = w.Service.RegionOperations.Get(w.Project, region, w.Op.Name).Do()
} else {
op, err = w.Service.GlobalOperations.Get(w.Project, w.Op.Name).Do()
}
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %q when asking for operation %q", op.Status, w.Op.Name)
return op, op.Status, nil
}
func (w *ComputeOperationWaiter) State() string {
return w.Op.Status
}
func (w *ComputeOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING", "RUNNING"},
Target: []string{"DONE"},
Refresh: w.RefreshFunc(),
func (w *ComputeOperationWaiter) Error() error {
if w.Op.Error != nil {
return ComputeOperationError(*w.Op.Error)
}
return nil
}
func (w *ComputeOperationWaiter) SetOp(op interface{}) error {
w.Op = op.(*compute.Operation)
return nil
}
func (w *ComputeOperationWaiter) QueryOp() (interface{}, error) {
if w.Op.Zone != "" {
zone := GetResourceNameFromSelfLink(w.Op.Zone)
return w.Service.ZoneOperations.Get(w.Project, zone, w.Op.Name).Do()
} else if w.Op.Region != "" {
region := GetResourceNameFromSelfLink(w.Op.Region)
return w.Service.RegionOperations.Get(w.Project, region, w.Op.Name).Do()
}
return w.Service.GlobalOperations.Get(w.Project, w.Op.Name).Do()
}
func (w *ComputeOperationWaiter) OpName() string {
return w.Op.Name
}
func (w *ComputeOperationWaiter) PendingStates() []string {
return []string{"PENDING", "RUNNING"}
}
func (w *ComputeOperationWaiter) TargetStates() []string {
return []string{"DONE"}
}
func computeOperationWait(client *compute.Service, op *compute.Operation, project, activity string) error {
return computeOperationWaitTime(client, op, project, activity, 4)
}
func computeOperationWaitTime(client *compute.Service, op *compute.Operation, project, activity string, timeoutMinutes int) error {
w := &ComputeOperationWaiter{
Service: client,
Op: op,
Project: project,
}
if err := w.SetOp(op); err != nil {
return err
}
return OperationWait(w, activity, timeoutMinutes)
}
func computeBetaOperationWaitTime(client *compute.Service, op *computeBeta.Operation, project, activity string, timeoutMin int) error {
opV1 := &compute.Operation{}
err := Convert(op, opV1)
if err != nil {
return err
}
return computeOperationWaitTime(client, opV1, project, activity, timeoutMin)
}
// ComputeOperationError wraps compute.OperationError and implements the
@ -61,48 +91,3 @@ func (e ComputeOperationError) Error() string {
return buf.String()
}
func computeOperationWait(client *compute.Service, op *compute.Operation, project, activity string) error {
return computeOperationWaitTime(client, op, project, activity, 4)
}
func computeOperationWaitTime(client *compute.Service, op *compute.Operation, project, activity string, timeoutMin int) error {
if op.Status == "DONE" {
if op.Error != nil {
return ComputeOperationError(*op.Error)
}
return nil
}
w := &ComputeOperationWaiter{
Service: client,
Op: op,
Project: project,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
resultOp := opRaw.(*compute.Operation)
if resultOp.Error != nil {
return ComputeOperationError(*resultOp.Error)
}
return nil
}
func computeBetaOperationWaitTime(client *compute.Service, op *computeBeta.Operation, project, activity string, timeoutMin int) error {
opV1 := &compute.Operation{}
err := Convert(op, opV1)
if err != nil {
return err
}
return computeOperationWaitTime(client, opV1, project, activity, timeoutMin)
}

View File

@ -2,142 +2,61 @@ package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/container/v1"
containerBeta "google.golang.org/api/container/v1beta1"
"google.golang.org/api/container/v1beta1"
)
type ContainerOperationWaiter struct {
Service *container.Service
Op *container.Operation
Project string
Zone string
}
type ContainerBetaOperationWaiter struct {
Service *containerBeta.Service
Op *containerBeta.Operation
Service *container.Service
Op *container.Operation
Project string
Location string
}
func (w *ContainerOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING", "RUNNING"},
Target: []string{"DONE"},
Refresh: w.RefreshFunc(),
}
func (w *ContainerOperationWaiter) State() string {
return w.Op.Status
}
func (w *ContainerBetaOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING", "RUNNING"},
Target: []string{"DONE"},
Refresh: w.RefreshFunc(),
func (w *ContainerOperationWaiter) Error() error {
if w.Op.StatusMessage != "" {
return fmt.Errorf(w.Op.StatusMessage)
}
return nil
}
func (w *ContainerOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := w.Service.Projects.Zones.Operations.Get(
w.Project, w.Zone, w.Op.Name).Do()
if err != nil {
return nil, "", err
}
if resp.StatusMessage != "" {
return resp, resp.Status, fmt.Errorf(resp.StatusMessage)
}
log.Printf("[DEBUG] Progress of operation %q: %q", w.Op.Name, resp.Status)
return resp, resp.Status, err
}
func (w *ContainerOperationWaiter) SetOp(op interface{}) error {
w.Op = op.(*container.Operation)
return nil
}
func (w *ContainerBetaOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
name := fmt.Sprintf("projects/%s/locations/%s/operations/%s",
w.Project, w.Location, w.Op.Name)
resp, err := w.Service.Projects.Locations.Operations.Get(name).Do()
if err != nil {
return nil, "", err
}
if resp.StatusMessage != "" {
return resp, resp.Status, fmt.Errorf(resp.StatusMessage)
}
log.Printf("[DEBUG] Progress of operation %q: %q", w.Op.Name, resp.Status)
return resp, resp.Status, err
}
func (w *ContainerOperationWaiter) QueryOp() (interface{}, error) {
name := fmt.Sprintf("projects/%s/locations/%s/operations/%s",
w.Project, w.Location, w.Op.Name)
return w.Service.Projects.Locations.Operations.Get(name).Do()
}
func containerOperationWait(config *Config, op *container.Operation, project, zone, activity string, timeoutMinutes, minTimeoutSeconds int) error {
if op.Status == "DONE" {
if op.StatusMessage != "" {
return fmt.Errorf(op.StatusMessage)
}
return nil
}
func (w *ContainerOperationWaiter) OpName() string {
return w.Op.Name
}
func (w *ContainerOperationWaiter) PendingStates() []string {
return []string{"PENDING", "RUNNING"}
}
func (w *ContainerOperationWaiter) TargetStates() []string {
return []string{"DONE"}
}
func containerOperationWait(config *Config, op *container.Operation, project, location, activity string, timeoutMinutes int) error {
w := &ContainerOperationWaiter{
Service: config.clientContainer,
Op: op,
Project: project,
Zone: zone,
}
state := w.Conf()
return waitForState(state, activity, timeoutMinutes, minTimeoutSeconds)
}
func containerBetaOperationWait(config *Config, op *containerBeta.Operation, project, location, activity string, timeoutMinutes, minTimeoutSeconds int) error {
if op.Status == "DONE" {
if op.StatusMessage != "" {
return fmt.Errorf(op.StatusMessage)
}
return nil
}
w := &ContainerBetaOperationWaiter{
Service: config.clientContainerBeta,
Op: op,
Project: project,
Location: location,
}
state := w.Conf()
return waitForState(state, activity, timeoutMinutes, minTimeoutSeconds)
}
func waitForState(state *resource.StateChangeConf, activity string, timeoutMinutes, minTimeoutSeconds int) error {
state.Timeout = time.Duration(timeoutMinutes) * time.Minute
state.MinTimeout = time.Duration(minTimeoutSeconds) * time.Second
_, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
return nil
}
func containerSharedOperationWait(config *Config, op interface{}, project, location, activity string, timeoutMinutes, minTimeoutSeconds int) error {
if op == nil {
panic("Attempted to wait on an Operation that was nil.")
}
switch op.(type) {
case *container.Operation:
return containerOperationWait(config, op.(*container.Operation), project, location, activity, timeoutMinutes, minTimeoutSeconds)
case *containerBeta.Operation:
return containerBetaOperationWait(config, op.(*containerBeta.Operation), project, location, activity, timeoutMinutes, minTimeoutSeconds)
default:
panic("Attempted to wait on an Operation of unknown type.")
if err := w.SetOp(op); err != nil {
return err
}
return OperationWait(w, activity, timeoutMinutes)
}

View File

@ -1,66 +1,24 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/dataproc/v1"
)
type DataprocClusterOperationWaiter struct {
Service *dataproc.Service
Op *dataproc.Operation
CommonOperationWaiter
}
func (w *DataprocClusterOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"false"},
Target: []string{"true"},
Refresh: w.RefreshFunc(),
}
func (w *DataprocClusterOperationWaiter) QueryOp() (interface{}, error) {
return w.Service.Projects.Regions.Operations.Get(w.Op.Name).Do()
}
func (w *DataprocClusterOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
op, err := w.Service.Projects.Regions.Operations.Get(w.Op.Name).Do()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %v while polling for operation %s's 'done' status", op.Done, w.Op.Name)
return op, fmt.Sprint(op.Done), nil
}
}
func dataprocClusterOperationWait(config *Config, op *dataproc.Operation, activity string, timeoutMinutes, minTimeoutSeconds int) error {
if op.Done {
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
}
func dataprocClusterOperationWait(config *Config, op *dataproc.Operation, activity string, timeoutMinutes int) error {
w := &DataprocClusterOperationWaiter{
Service: config.clientDataproc,
Op: op,
}
state := w.Conf()
state.Timeout = time.Duration(timeoutMinutes) * time.Minute
state.MinTimeout = time.Duration(minTimeoutSeconds) * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
if err := w.SetOp(op); err != nil {
return err
}
op = opRaw.(*dataproc.Operation)
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
return OperationWait(w, activity, timeoutMinutes)
}

View File

@ -1,14 +1,9 @@
package google
import (
"fmt"
"time"
"net/http"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/dataproc/v1"
"google.golang.org/api/googleapi"
)
type DataprocJobOperationWaiter struct {
@ -16,80 +11,46 @@ type DataprocJobOperationWaiter struct {
Region string
ProjectId string
JobId string
Status string
}
func (w *DataprocJobOperationWaiter) ConfForDelete() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"EXISTS"},
Target: []string{"DELETED"},
Refresh: w.RefreshFuncForDelete(),
}
func (w *DataprocJobOperationWaiter) State() string {
return w.Status
}
func (w *DataprocJobOperationWaiter) Conf() *resource.StateChangeConf {
// For more info on each of the states please see
// https://cloud.google.com/dataproc/docs/reference/rest/v1/projects.regions.jobs#JobStatus
return &resource.StateChangeConf{
Pending: []string{"PENDING", "CANCEL_PENDING", "CANCEL_STARTED", "SETUP_DONE", "RUNNING"},
Target: []string{"CANCELLED", "DONE", "ATTEMPT_FAILURE", "ERROR"},
Refresh: w.RefreshFunc(),
}
}
func isNotFound(err error) bool {
if err == nil {
return false
}
ae, ok := err.(*googleapi.Error)
return ok && ae.Code == http.StatusNotFound
}
func (w *DataprocJobOperationWaiter) RefreshFuncForDelete() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
_, err := w.Service.Projects.Regions.Jobs.Get(w.ProjectId, w.Region, w.JobId).Do()
if err != nil {
if isNotFound(err) {
return "NA", "DELETED", nil
}
return nil, "", err
}
return "JOB", "EXISTS", err
}
}
func (w *DataprocJobOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
job, err := w.Service.Projects.Regions.Jobs.Get(w.ProjectId, w.Region, w.JobId).Do()
if err != nil {
return nil, "", err
}
return job, job.Status.State, err
}
}
func dataprocDeleteOperationWait(config *Config, region, projectId, jobId string, activity string, timeoutMinutes, minTimeoutSeconds int) error {
w := &DataprocJobOperationWaiter{
Service: config.clientDataproc,
Region: region,
ProjectId: projectId,
JobId: jobId,
}
state := w.ConfForDelete()
state.Timeout = time.Duration(timeoutMinutes) * time.Minute
state.MinTimeout = time.Duration(minTimeoutSeconds) * time.Second
_, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
func (w *DataprocJobOperationWaiter) Error() error {
// The "operation" is just the job, which has no special error field that we
// want to expose.
return nil
}
func (w *DataprocJobOperationWaiter) SetOp(job interface{}) error {
// The "operation" is just the job. Instead of holding onto the whole job
// object, we only care about the state, which gets set in QueryOp, so this
// doesn't have to do anything.
return nil
}
func (w *DataprocJobOperationWaiter) QueryOp() (interface{}, error) {
job, err := w.Service.Projects.Regions.Jobs.Get(w.ProjectId, w.Region, w.JobId).Do()
if job != nil {
w.Status = job.Status.State
}
return job, err
}
func (w *DataprocJobOperationWaiter) OpName() string {
return w.JobId
}
func (w *DataprocJobOperationWaiter) PendingStates() []string {
return []string{"PENDING", "CANCEL_PENDING", "CANCEL_STARTED", "SETUP_DONE", "RUNNING"}
}
func (w *DataprocJobOperationWaiter) TargetStates() []string {
return []string{"CANCELLED", "DONE", "ATTEMPT_FAILURE", "ERROR"}
}
func dataprocJobOperationWait(config *Config, region, projectId, jobId string, activity string, timeoutMinutes, minTimeoutSeconds int) error {
w := &DataprocJobOperationWaiter{
Service: config.clientDataproc,
@ -97,14 +58,42 @@ func dataprocJobOperationWait(config *Config, region, projectId, jobId string, a
ProjectId: projectId,
JobId: jobId,
}
state := w.Conf()
state.Timeout = time.Duration(timeoutMinutes) * time.Minute
state.MinTimeout = time.Duration(minTimeoutSeconds) * time.Second
_, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for operation %s: %s", activity, err)
}
return nil
return OperationWait(w, activity, timeoutMinutes)
}
type DataprocDeleteJobOperationWaiter struct {
DataprocJobOperationWaiter
}
func (w *DataprocDeleteJobOperationWaiter) PendingStates() []string {
return []string{"EXISTS", "ERROR"}
}
func (w *DataprocDeleteJobOperationWaiter) TargetStates() []string {
return []string{"DELETED"}
}
func (w *DataprocDeleteJobOperationWaiter) QueryOp() (interface{}, error) {
job, err := w.Service.Projects.Regions.Jobs.Get(w.ProjectId, w.Region, w.JobId).Do()
if err != nil {
if isGoogleApiErrorWithCode(err, http.StatusNotFound) {
w.Status = "DELETED"
return job, nil
}
w.Status = "ERROR"
}
w.Status = "EXISTS"
return job, err
}
func dataprocDeleteOperationWait(config *Config, region, projectId, jobId string, activity string, timeoutMinutes, minTimeoutSeconds int) error {
w := &DataprocDeleteJobOperationWaiter{
DataprocJobOperationWaiter{
Service: config.clientDataproc,
Region: region,
ProjectId: projectId,
JobId: jobId,
},
}
return OperationWait(w, activity, timeoutMinutes)
}

View File

@ -32,14 +32,11 @@ func (w *DnsChangeWaiter) RefreshFunc() resource.StateRefreshFunc {
}
func (w *DnsChangeWaiter) Conf() *resource.StateChangeConf {
state := &resource.StateChangeConf{
Pending: []string{"pending"},
Target: []string{"done"},
Refresh: w.RefreshFunc(),
return &resource.StateChangeConf{
Pending: []string{"pending"},
Target: []string{"done"},
Refresh: w.RefreshFunc(),
Timeout: 10 * time.Minute,
MinTimeout: 2 * time.Second,
}
state.Delay = 10 * time.Second
state.Timeout = 10 * time.Minute
state.MinTimeout = 2 * time.Second
return state
}

View File

@ -1,67 +1,24 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/redis/v1beta1"
)
type RedisOperationWaiter struct {
Service *redis.ProjectsLocationsService
Op *redis.Operation
CommonOperationWaiter
}
func (w *RedisOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
op, err := w.Service.Operations.Get(w.Op.Name).Do()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %v while polling for operation %s's 'done' status", op.Done, w.Op.Name)
return op, fmt.Sprint(op.Done), nil
}
func (w *RedisOperationWaiter) QueryOp() (interface{}, error) {
return w.Service.Operations.Get(w.Op.Name).Do()
}
func (w *RedisOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"false"},
Target: []string{"true"},
Refresh: w.RefreshFunc(),
}
}
func redisOperationWaitTime(service *redis.Service, op *redis.Operation, project, activity string, timeoutMin int) error {
if op.Done {
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
}
func redisOperationWaitTime(service *redis.Service, op *redis.Operation, project, activity string, timeoutMinutes int) error {
w := &RedisOperationWaiter{
Service: service.Projects.Locations,
Op: op,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
if err := w.SetOp(op); err != nil {
return err
}
op = opRaw.(*redis.Operation)
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
return OperationWait(w, activity, timeoutMinutes)
}

View File

@ -715,7 +715,7 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
defer mutexKV.Unlock(containerClusterMutexKey(project, location, clusterName))
parent := fmt.Sprintf("projects/%s/locations/%s", project, location)
var op interface{}
var op *containerBeta.Operation
err = retry(func() error {
op, err = config.clientContainerBeta.Projects.Locations.Clusters.Create(parent, req).Do()
return err
@ -728,7 +728,7 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
// Wait until it's created
timeoutInMinutes := int(d.Timeout(schema.TimeoutCreate).Minutes())
waitErr := containerSharedOperationWait(config, op, project, location, "creating GKE cluster", timeoutInMinutes, 3)
waitErr := containerOperationWait(config, op, project, location, "creating GKE cluster", timeoutInMinutes)
if waitErr != nil {
// The resource didn't actually create
d.SetId("")
@ -743,7 +743,7 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
if err != nil {
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
}
err = containerSharedOperationWait(config, op, project, location, "removing default node pool", timeoutInMinutes, 3)
err = containerOperationWait(config, op, project, location, "removing default node pool", timeoutInMinutes)
if err != nil {
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
}
@ -878,7 +878,7 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
return err
}
// Wait until it's updated
return containerSharedOperationWait(config, op, project, location, updateDescription, timeoutInMinutes, 2)
return containerOperationWait(config, op, project, location, updateDescription, timeoutInMinutes)
}
}
@ -993,7 +993,7 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
}
// Wait until it's updated
return containerSharedOperationWait(config, op, project, location, "updating GKE cluster maintenance policy", timeoutInMinutes, 2)
return containerOperationWait(config, op, project, location, "updating GKE cluster maintenance policy", timeoutInMinutes)
}
// Call update serially.
@ -1071,7 +1071,7 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
}
// Wait until it's updated
err = containerSharedOperationWait(config, op, project, location, "updating GKE legacy ABAC", timeoutInMinutes, 2)
err = containerOperationWait(config, op, project, location, "updating GKE legacy ABAC", timeoutInMinutes)
log.Println("[DEBUG] done updating enable_legacy_abac")
return err
}
@ -1120,7 +1120,7 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
}
// Wait until it's updated
return containerSharedOperationWait(config, op, project, location, "updating GKE logging service", timeoutInMinutes, 2)
return containerOperationWait(config, op, project, location, "updating GKE logging service", timeoutInMinutes)
}
// Call update serially.
@ -1148,7 +1148,7 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
}
// Wait until it's updated
err = containerSharedOperationWait(config, op, project, location, "updating GKE cluster network policy", timeoutInMinutes, 2)
err = containerOperationWait(config, op, project, location, "updating GKE cluster network policy", timeoutInMinutes)
log.Println("[DEBUG] done updating network_policy")
return err
}
@ -1195,7 +1195,7 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
}
// Wait until it's updated
return containerSharedOperationWait(config, op, project, location, "updating GKE image type", timeoutInMinutes, 2)
return containerOperationWait(config, op, project, location, "updating GKE image type", timeoutInMinutes)
}
// Call update serially.
@ -1232,7 +1232,7 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
}
// Wait until it's updated
return containerSharedOperationWait(config, op, project, location, "updating master auth", timeoutInMinutes, 2)
return containerOperationWait(config, op, project, location, "updating master auth", timeoutInMinutes)
}
// Call update serially.
@ -1257,7 +1257,7 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
}
// Wait until it's updated
return containerSharedOperationWait(config, op, project, location, "updating GKE resource labels", timeoutInMinutes, 2)
return containerOperationWait(config, op, project, location, "updating GKE resource labels", timeoutInMinutes)
}
// Call update serially.
@ -1274,7 +1274,7 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
if err != nil {
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
}
err = containerSharedOperationWait(config, op, project, location, "removing default node pool", timeoutInMinutes, 3)
err = containerOperationWait(config, op, project, location, "removing default node pool", timeoutInMinutes)
if err != nil {
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
}
@ -1312,7 +1312,7 @@ func resourceContainerClusterDelete(d *schema.ResourceData, meta interface{}) er
mutexKV.Lock(containerClusterMutexKey(project, location, clusterName))
defer mutexKV.Unlock(containerClusterMutexKey(project, location, clusterName))
var op interface{}
var op *containerBeta.Operation
var count = 0
err = resource.Retry(30*time.Second, func() *resource.RetryError {
count++
@ -1336,7 +1336,7 @@ func resourceContainerClusterDelete(d *schema.ResourceData, meta interface{}) er
}
// Wait until it's deleted
waitErr := containerSharedOperationWait(config, op, project, location, "deleting GKE cluster", timeoutInMinutes, 3)
waitErr := containerOperationWait(config, op, project, location, "deleting GKE cluster", timeoutInMinutes)
if waitErr != nil {
return waitErr
}

View File

@ -251,9 +251,9 @@ func resourceContainerNodePoolCreate(d *schema.ResourceData, meta interface{}) e
d.SetId(fmt.Sprintf("%s/%s/%s", nodePoolInfo.location, nodePoolInfo.cluster, nodePool.Name))
waitErr := containerBetaOperationWait(config,
waitErr := containerOperationWait(config,
operation, nodePoolInfo.project,
nodePoolInfo.location, "creating GKE NodePool", int(timeout.Minutes()), 3)
nodePoolInfo.location, "creating GKE NodePool", int(timeout.Minutes()))
if waitErr != nil {
// The resource didn't actually create
@ -369,7 +369,7 @@ func resourceContainerNodePoolDelete(d *schema.ResourceData, meta interface{}) e
}
// Wait until it's deleted
waitErr := containerBetaOperationWait(config, op, nodePoolInfo.project, nodePoolInfo.location, "deleting GKE NodePool", timeoutInMinutes, 2)
waitErr := containerOperationWait(config, op, nodePoolInfo.project, nodePoolInfo.location, "deleting GKE NodePool", timeoutInMinutes)
if waitErr != nil {
return waitErr
}
@ -571,10 +571,10 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node
}
// Wait until it's updated
return containerBetaOperationWait(config, op,
return containerOperationWait(config, op,
nodePoolInfo.project,
nodePoolInfo.location, "updating GKE node pool",
timeoutInMinutes, 2)
timeoutInMinutes)
}
// Call update serially.
@ -605,10 +605,10 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node
}
// Wait until it's updated
return containerBetaOperationWait(config, op,
return containerOperationWait(config, op,
nodePoolInfo.project,
nodePoolInfo.location, "updating GKE node pool",
timeoutInMinutes, 2)
timeoutInMinutes)
}
// Call update serially.
@ -637,10 +637,10 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node
}
// Wait until it's updated
return containerBetaOperationWait(config, op,
return containerOperationWait(config, op,
nodePoolInfo.project,
nodePoolInfo.location, "updating GKE node pool size",
timeoutInMinutes, 2)
timeoutInMinutes)
}
// Call update serially.
@ -676,9 +676,9 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node
}
// Wait until it's updated
return containerBetaOperationWait(config, op,
return containerOperationWait(config, op,
nodePoolInfo.project,
nodePoolInfo.location, "updating GKE node pool management", timeoutInMinutes, 2)
nodePoolInfo.location, "updating GKE node pool management", timeoutInMinutes)
}
// Call update serially.
@ -707,9 +707,9 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node
}
// Wait until it's updated
return containerBetaOperationWait(config, op,
return containerOperationWait(config, op,
nodePoolInfo.project,
nodePoolInfo.location, "updating GKE node pool version", timeoutInMinutes, 2)
nodePoolInfo.location, "updating GKE node pool version", timeoutInMinutes)
}
// Call update serially.

View File

@ -456,7 +456,7 @@ func resourceDataprocClusterCreate(d *schema.ResourceData, meta interface{}) err
// Wait until it's created
timeoutInMinutes := int(d.Timeout(schema.TimeoutCreate).Minutes())
waitErr := dataprocClusterOperationWait(config, op, "creating Dataproc cluster", timeoutInMinutes, 3)
waitErr := dataprocClusterOperationWait(config, op, "creating Dataproc cluster", timeoutInMinutes)
if waitErr != nil {
// The resource didn't actually create
// Note that we do not remove the ID here - this resource tends to leave
@ -740,7 +740,7 @@ func resourceDataprocClusterUpdate(d *schema.ResourceData, meta interface{}) err
}
// Wait until it's updated
waitErr := dataprocClusterOperationWait(config, op, "updating Dataproc cluster ", timeoutInMinutes, 2)
waitErr := dataprocClusterOperationWait(config, op, "updating Dataproc cluster ", timeoutInMinutes)
if waitErr != nil {
return waitErr
}
@ -944,7 +944,7 @@ func resourceDataprocClusterDelete(d *schema.ResourceData, meta interface{}) err
}
// Wait until it's deleted
waitErr := dataprocClusterOperationWait(config, op, "deleting Dataproc cluster", timeoutInMinutes, 3)
waitErr := dataprocClusterOperationWait(config, op, "deleting Dataproc cluster", timeoutInMinutes)
if waitErr != nil {
return waitErr
}

View File

@ -280,7 +280,7 @@ func enableServices(s []string, pid string, config *Config) error {
// Poll for the API to return
activity := fmt.Sprintf("apis %q to be enabled for %s", services, pid)
_, waitErr := serviceUsageOperationWait(config, sop, activity)
waitErr := serviceUsageOperationWait(config, sop, activity)
if waitErr != nil {
return waitErr
}
@ -342,7 +342,7 @@ func disableService(s, pid string, config *Config) error {
return err
}
// Wait for the operation to complete
_, waitErr := serviceUsageOperationWait(config, sop, "api to disable")
waitErr := serviceUsageOperationWait(config, sop, "api to disable")
if waitErr != nil {
return waitErr
}

View File

@ -1,76 +1,33 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/cloudresourcemanager/v1"
resourceManagerV2Beta1 "google.golang.org/api/cloudresourcemanager/v2beta1"
)
type ResourceManagerOperationWaiter struct {
Service *cloudresourcemanager.Service
Op *cloudresourcemanager.Operation
CommonOperationWaiter
}
func (w *ResourceManagerOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
op, err := w.Service.Operations.Get(w.Op.Name).Do()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %v while polling for operation %s's 'done' status", op.Done, w.Op.Name)
return op, fmt.Sprint(op.Done), nil
}
func (w *ResourceManagerOperationWaiter) QueryOp() (interface{}, error) {
return w.Service.Operations.Get(w.Op.Name).Do()
}
func (w *ResourceManagerOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"false"},
Target: []string{"true"},
Refresh: w.RefreshFunc(),
func resourceManagerOperationWaitTime(service *cloudresourcemanager.Service, op *cloudresourcemanager.Operation, activity string, timeoutMin int) error {
w := &ResourceManagerOperationWaiter{
Service: service,
}
if err := w.SetOp(op); err != nil {
return err
}
return OperationWait(w, activity, timeoutMin)
}
func resourceManagerOperationWait(service *cloudresourcemanager.Service, op *cloudresourcemanager.Operation, activity string) error {
return resourceManagerOperationWaitTime(service, op, activity, 4)
}
func resourceManagerOperationWaitTime(service *cloudresourcemanager.Service, op *cloudresourcemanager.Operation, activity string, timeoutMin int) error {
if op.Done {
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
}
w := &ResourceManagerOperationWaiter{
Service: service,
Op: op,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
op = opRaw.(*cloudresourcemanager.Operation)
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
}
func resourceManagerV2Beta1OperationWait(service *cloudresourcemanager.Service, op *resourceManagerV2Beta1.Operation, activity string) error {
return resourceManagerV2Beta1OperationWaitTime(service, op, activity, 4)
}

View File

@ -33,26 +33,21 @@ func (w *ServiceAccountKeyWaiter) RefreshFunc() resource.StateRefreshFunc {
}
}
func (w *ServiceAccountKeyWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING"},
Target: []string{"DONE"},
Refresh: w.RefreshFunc(),
}
}
func serviceAccountKeyWaitTime(client *iam.ProjectsServiceAccountsKeysService, keyName, publicKeyType, activity string, timeoutMin int) error {
func serviceAccountKeyWaitTime(client *iam.ProjectsServiceAccountsKeysService, keyName, publicKeyType, activity string, timeoutMinutes int) error {
w := &ServiceAccountKeyWaiter{
Service: client,
PublicKeyType: publicKeyType,
KeyName: keyName,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
_, err := state.WaitForState()
c := &resource.StateChangeConf{
Pending: []string{"PENDING"},
Target: []string{"DONE"},
Refresh: w.RefreshFunc(),
Timeout: time.Duration(timeoutMinutes) * time.Minute,
MinTimeout: 2 * time.Second,
}
_, err := c.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}

View File

@ -1,75 +1,34 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/googleapi"
"google.golang.org/api/servicemanagement/v1"
)
type ServiceManagementOperationWaiter struct {
Service *servicemanagement.APIService
Op *servicemanagement.Operation
CommonOperationWaiter
}
func (w *ServiceManagementOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var op *servicemanagement.Operation
var err error
op, err = w.Service.Operations.Get(w.Op.Name).Do()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %v while polling for operation %s's 'done' status", op.Done, w.Op.Name)
return op, fmt.Sprint(op.Done), nil
}
}
func (w *ServiceManagementOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"false"},
Target: []string{"true"},
Refresh: w.RefreshFunc(),
}
func (w *ServiceManagementOperationWaiter) QueryOp() (interface{}, error) {
return w.Service.Operations.Get(w.Op.Name).Do()
}
func serviceManagementOperationWait(config *Config, op *servicemanagement.Operation, activity string) (googleapi.RawMessage, error) {
return serviceManagementOperationWaitTime(config, op, activity, 10)
}
func serviceManagementOperationWaitTime(config *Config, op *servicemanagement.Operation, activity string, timeoutMin int) (googleapi.RawMessage, error) {
if op.Done {
if op.Error != nil {
return nil, fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return op.Response, nil
}
func serviceManagementOperationWaitTime(config *Config, op *servicemanagement.Operation, activity string, timeoutMinutes int) (googleapi.RawMessage, error) {
w := &ServiceManagementOperationWaiter{
Service: config.clientServiceMan,
Op: op,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return nil, fmt.Errorf("Error waiting for %s: %s", activity, err)
if err := w.SetOp(op); err != nil {
return nil, err
}
op = opRaw.(*servicemanagement.Operation)
if op.Error != nil {
return nil, fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
if err := OperationWait(w, activity, timeoutMinutes); err != nil {
return nil, err
}
return op.Response, nil
return w.Op.Response, nil
}

View File

@ -1,75 +1,28 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/googleapi"
"google.golang.org/api/serviceusage/v1beta1"
)
type serviceUsageOperationWaiter struct {
type ServiceUsageOperationWaiter struct {
Service *serviceusage.APIService
Op *serviceusage.Operation
CommonOperationWaiter
}
func (w *serviceUsageOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var op *serviceusage.Operation
var err error
op, err = w.Service.Operations.Get(w.Op.Name).Do()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %v while polling for operation %s's 'done' status", op.Done, w.Op.Name)
return op, fmt.Sprint(op.Done), nil
}
func (w *ServiceUsageOperationWaiter) QueryOp() (interface{}, error) {
return w.Service.Operations.Get(w.Op.Name).Do()
}
func (w *serviceUsageOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"false"},
Target: []string{"true"},
Refresh: w.RefreshFunc(),
}
}
func serviceUsageOperationWait(config *Config, op *serviceusage.Operation, activity string) (googleapi.RawMessage, error) {
func serviceUsageOperationWait(config *Config, op *serviceusage.Operation, activity string) error {
return serviceUsageOperationWaitTime(config, op, activity, 10)
}
func serviceUsageOperationWaitTime(config *Config, op *serviceusage.Operation, activity string, timeoutMin int) (googleapi.RawMessage, error) {
if op.Done {
if op.Error != nil {
return nil, fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return op.Response, nil
}
w := &serviceUsageOperationWaiter{
func serviceUsageOperationWaitTime(config *Config, op *serviceusage.Operation, activity string, timeoutMinutes int) error {
w := &ServiceUsageOperationWaiter{
Service: config.clientServiceUsage,
Op: op,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return nil, fmt.Errorf("Error waiting for %s: %s", activity, err)
if err := w.SetOp(op); err != nil {
return err
}
op = opRaw.(*serviceusage.Operation)
if op.Error != nil {
return nil, fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return op.Response, nil
return OperationWait(w, activity, timeoutMinutes)
}

View File

@ -1,69 +1,25 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/spanner/v1"
)
type SpannerDatabaseOperationWaiter struct {
Service *spanner.Service
Op *spanner.Operation
CommonOperationWaiter
}
func (w *SpannerDatabaseOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"false"},
Target: []string{"true"},
Refresh: w.RefreshFunc(),
}
func (w *SpannerDatabaseOperationWaiter) QueryOp() (interface{}, error) {
return w.Service.Projects.Instances.Databases.Operations.Get(w.Op.Name).Do()
}
func (w *SpannerDatabaseOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
op, err := w.Service.Projects.Instances.Databases.Operations.Get(w.Op.Name).Do()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %v while polling for operation %s's 'done' status", op.Done, w.Op.Name)
return op, fmt.Sprint(op.Done), nil
}
}
func spannerDatabaseOperationWait(config *Config, op *spanner.Operation, activity string, timeoutMin int) error {
if op.Done {
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
}
func spannerDatabaseOperationWait(config *Config, op *spanner.Operation, activity string, timeoutMinutes int) error {
w := &SpannerDatabaseOperationWaiter{
Service: config.clientSpanner,
Op: op,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
if err := w.SetOp(op); err != nil {
return err
}
op = opRaw.(*spanner.Operation)
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
return OperationWait(w, activity, timeoutMinutes)
}

View File

@ -1,69 +1,24 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/spanner/v1"
)
type SpannerInstanceOperationWaiter struct {
Service *spanner.Service
Op *spanner.Operation
CommonOperationWaiter
}
func (w *SpannerInstanceOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"false"},
Target: []string{"true"},
Refresh: w.RefreshFunc(),
}
func (w *SpannerInstanceOperationWaiter) QueryOp() (interface{}, error) {
return w.Service.Projects.Instances.Operations.Get(w.Op.Name).Do()
}
func (w *SpannerInstanceOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
op, err := w.Service.Projects.Instances.Operations.Get(w.Op.Name).Do()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %v while polling for operation %s's 'done' status", op.Done, w.Op.Name)
return op, fmt.Sprint(op.Done), nil
}
}
func spannerInstanceOperationWait(config *Config, op *spanner.Operation, activity string, timeoutMin int) error {
if op.Done {
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
}
func spannerInstanceOperationWait(config *Config, op *spanner.Operation, activity string, timeoutMinutes int) error {
w := &SpannerInstanceOperationWaiter{
Service: config.clientSpanner,
Op: op,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = time.Duration(timeoutMin) * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
if err := w.SetOp(op); err != nil {
return err
}
op = opRaw.(*spanner.Operation)
if op.Error != nil {
return fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
}
return nil
return OperationWait(w, activity, timeoutMinutes)
}

View File

@ -2,12 +2,7 @@ package google
import (
"bytes"
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/googleapi"
"google.golang.org/api/sqladmin/v1beta4"
)
@ -17,29 +12,52 @@ type SqlAdminOperationWaiter struct {
Project string
}
func (w *SqlAdminOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
log.Printf("[DEBUG] self_link: %s", w.Op.SelfLink)
op, err := w.Service.Operations.Get(w.Project, w.Op.Name).Do()
if e, ok := err.(*googleapi.Error); ok && (e.Code == 429 || e.Code == 503) {
return w.Op, "PENDING", nil
} else if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %q when asking for operation %q", op.Status, w.Op.Name)
return op, op.Status, nil
}
func (w *SqlAdminOperationWaiter) State() string {
return w.Op.Status
}
func (w *SqlAdminOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING", "RUNNING"},
Target: []string{"DONE"},
Refresh: w.RefreshFunc(),
func (w *SqlAdminOperationWaiter) Error() error {
if w.Op.Error != nil {
return SqlAdminOperationError(*w.Op.Error)
}
return nil
}
func (w *SqlAdminOperationWaiter) SetOp(op interface{}) error {
w.Op = op.(*sqladmin.Operation)
return nil
}
func (w *SqlAdminOperationWaiter) QueryOp() (interface{}, error) {
return w.Service.Operations.Get(w.Project, w.Op.Name).Do()
}
func (w *SqlAdminOperationWaiter) OpName() string {
return w.Op.Name
}
func (w *SqlAdminOperationWaiter) PendingStates() []string {
return []string{"PENDING", "RUNNING"}
}
func (w *SqlAdminOperationWaiter) TargetStates() []string {
return []string{"DONE"}
}
func sqladminOperationWait(config *Config, op *sqladmin.Operation, project, activity string) error {
return sqladminOperationWaitTime(config, op, project, activity, 10)
}
func sqladminOperationWaitTime(config *Config, op *sqladmin.Operation, project, activity string, timeoutMinutes int) error {
w := &SqlAdminOperationWaiter{
Service: config.clientSqlAdmin,
Op: op,
Project: project,
}
if err := w.SetOp(op); err != nil {
return err
}
return OperationWait(w, activity, timeoutMinutes)
}
// SqlAdminOperationError wraps sqladmin.OperationError and implements the
@ -55,38 +73,3 @@ func (e SqlAdminOperationError) Error() string {
return buf.String()
}
func sqladminOperationWait(config *Config, op *sqladmin.Operation, project, activity string) error {
return sqladminOperationWaitTime(config, op, project, activity, 10)
}
func sqladminOperationWaitTime(config *Config, op *sqladmin.Operation, project, activity string, timeoutMinutes int) error {
if op.Status == "DONE" {
if op.Error != nil {
return SqlAdminOperationError(*op.Error)
}
return nil
}
w := &SqlAdminOperationWaiter{
Service: config.clientSqlAdmin,
Op: op,
Project: project,
}
state := w.Conf()
state.Timeout = time.Duration(timeoutMinutes) * time.Minute
state.MinTimeout = 2 * time.Second
state.Delay = 5 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s (op %s): %s", activity, op.Name, err)
}
op = opRaw.(*sqladmin.Operation)
if op.Error != nil {
return SqlAdminOperationError(*op.Error)
}
return nil
}

View File

@ -335,14 +335,21 @@ func retryTimeDuration(retryFunc func() error, duration time.Duration) error {
return nil
}
for _, e := range errwrap.GetAllType(err, &googleapi.Error{}) {
if gerr, ok := e.(*googleapi.Error); ok && (gerr.Code == 429 || gerr.Code == 500 || gerr.Code == 502 || gerr.Code == 503) {
return resource.RetryableError(gerr)
if isRetryableError(e) {
return resource.RetryableError(e)
}
}
return resource.NonRetryableError(err)
})
}
func isRetryableError(err error) bool {
if gerr, ok := err.(*googleapi.Error); ok && (gerr.Code == 429 || gerr.Code == 500 || gerr.Code == 502 || gerr.Code == 503) {
return true
}
return false
}
func extractFirstMapConfig(m []interface{}) map[string]interface{} {
if len(m) == 0 {
return map[string]interface{}{}