terraform-provider-google/google/common_operation.go
The Magician 6e57eff780 Add support for Firestore Index (#3484)
Signed-off-by: Modular Magician <magic-modules@google.com>
2019-04-25 10:12:48 -07:00

156 lines
3.8 KiB
Go

package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1"
"google.golang.org/api/googleapi"
)
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. It's expected that QueryOp will return exactly one of an
// operation or an error as non-nil, and that requests will be retried by
// specific implementations of the method.
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 {
if w == nil {
return fmt.Sprintf("Operation is nil!")
}
return fmt.Sprintf("done: %v", w.Op.Done)
}
func (w *CommonOperationWaiter) Error() error {
if w != nil && 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 {
if w == nil {
return "<nil>"
}
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) {
op, err := w.QueryOp()
if err != nil {
// Importantly, this error is in the GET to the operation, and isn't an error
// with the resource CRUD request itself.
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
log.Printf("[DEBUG] Dismissed an operation GET as retryable based on error code being 404: %s", err)
return op, "done: false", nil
}
return nil, "", fmt.Errorf("error while retrieving operation: %s", err)
}
if err = w.SetOp(op); err != nil {
return nil, "", fmt.Errorf("Cannot continue, unable to use operation: %s", err)
}
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