...

Source file src/cloud.google.com/go/longrunning/longrunning.go

Documentation: cloud.google.com/go/longrunning

     1  // Copyright 2016 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package longrunning supports Long Running Operations for the Google Cloud Libraries.
    16  // See google.golang.org/genproto/googleapis/longrunning for its service definition.
    17  //
    18  // Users of the Google Cloud Libraries will typically not use this package directly.
    19  // Instead they will call functions returning Operations and call their methods.
    20  //
    21  // This package is still experimental and subject to change.
    22  package longrunning // import "cloud.google.com/go/longrunning"
    23  
    24  import (
    25  	"context"
    26  	"errors"
    27  	"fmt"
    28  	"time"
    29  
    30  	autogen "cloud.google.com/go/longrunning/autogen"
    31  	pb "cloud.google.com/go/longrunning/autogen/longrunningpb"
    32  	gax "github.com/googleapis/gax-go/v2"
    33  	"github.com/googleapis/gax-go/v2/apierror"
    34  	"google.golang.org/grpc/status"
    35  	"google.golang.org/protobuf/proto"
    36  	"google.golang.org/protobuf/protoadapt"
    37  	"google.golang.org/protobuf/types/known/anypb"
    38  )
    39  
    40  // ErrNoMetadata is the error returned by Metadata if the operation contains no metadata.
    41  var ErrNoMetadata = errors.New("operation contains no metadata")
    42  
    43  // Operation represents the result of an API call that may not be ready yet.
    44  type Operation struct {
    45  	c     operationsClient
    46  	proto *pb.Operation
    47  }
    48  
    49  type operationsClient interface {
    50  	GetOperation(context.Context, *pb.GetOperationRequest, ...gax.CallOption) (*pb.Operation, error)
    51  	CancelOperation(context.Context, *pb.CancelOperationRequest, ...gax.CallOption) error
    52  	DeleteOperation(context.Context, *pb.DeleteOperationRequest, ...gax.CallOption) error
    53  }
    54  
    55  // InternalNewOperation is for use by the google Cloud Libraries only.
    56  //
    57  // InternalNewOperation returns an long-running operation, abstracting the raw pb.Operation.
    58  // The conn parameter refers to a server that proto was received from.
    59  func InternalNewOperation(inner *autogen.OperationsClient, proto *pb.Operation) *Operation {
    60  	return &Operation{
    61  		c:     inner,
    62  		proto: proto,
    63  	}
    64  }
    65  
    66  // Name returns the name of the long-running operation.
    67  // The name is assigned by the server and is unique within the service
    68  // from which the operation is created.
    69  func (op *Operation) Name() string {
    70  	return op.proto.Name
    71  }
    72  
    73  // Done reports whether the long-running operation has completed.
    74  func (op *Operation) Done() bool {
    75  	return op.proto.Done
    76  }
    77  
    78  // Metadata unmarshals op's metadata into meta.
    79  // If op does not contain any metadata, Metadata returns ErrNoMetadata and meta is unmodified.
    80  func (op *Operation) Metadata(meta protoadapt.MessageV1) error {
    81  	if m := op.proto.Metadata; m != nil {
    82  		metav2 := protoadapt.MessageV2Of(meta)
    83  		return anypb.UnmarshalTo(m, metav2, proto.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true})
    84  	}
    85  	return ErrNoMetadata
    86  }
    87  
    88  // Poll fetches the latest state of a long-running operation.
    89  //
    90  // If Poll fails, the error is returned and op is unmodified.
    91  // If Poll succeeds and the operation has completed with failure,
    92  // the error is returned and op.Done will return true.
    93  // If Poll succeeds and the operation has completed successfully,
    94  // op.Done will return true; if resp != nil, the response of the operation
    95  // is stored in resp.
    96  func (op *Operation) Poll(ctx context.Context, resp protoadapt.MessageV1, opts ...gax.CallOption) error {
    97  	if !op.Done() {
    98  		p, err := op.c.GetOperation(ctx, &pb.GetOperationRequest{Name: op.Name()}, opts...)
    99  		if err != nil {
   100  			return err
   101  		}
   102  		op.proto = p
   103  	}
   104  	if !op.Done() {
   105  		return nil
   106  	}
   107  
   108  	switch r := op.proto.Result.(type) {
   109  	case *pb.Operation_Error:
   110  		err, _ := apierror.FromError(status.ErrorProto(r.Error))
   111  		return err
   112  	case *pb.Operation_Response:
   113  		if resp == nil {
   114  			return nil
   115  		}
   116  		respv2 := protoadapt.MessageV2Of(resp)
   117  		return anypb.UnmarshalTo(r.Response, respv2, proto.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true})
   118  	default:
   119  		return fmt.Errorf("unsupported result type %[1]T: %[1]v", r)
   120  	}
   121  }
   122  
   123  // DefaultWaitInterval is the polling interval used by Operation.Wait.
   124  const DefaultWaitInterval = 60 * time.Second
   125  
   126  // Wait is equivalent to WaitWithInterval using DefaultWaitInterval.
   127  func (op *Operation) Wait(ctx context.Context, resp protoadapt.MessageV1, opts ...gax.CallOption) error {
   128  	return op.WaitWithInterval(ctx, resp, DefaultWaitInterval, opts...)
   129  }
   130  
   131  // WaitWithInterval blocks until the operation is completed.
   132  // If resp != nil, Wait stores the response in resp.
   133  // WaitWithInterval polls every interval, except initially
   134  // when it polls using exponential backoff.
   135  //
   136  // See documentation of Poll for error-handling information.
   137  func (op *Operation) WaitWithInterval(ctx context.Context, resp protoadapt.MessageV1, interval time.Duration, opts ...gax.CallOption) error {
   138  	bo := gax.Backoff{
   139  		Initial: 1 * time.Second,
   140  		Max:     interval,
   141  	}
   142  	if bo.Max < bo.Initial {
   143  		bo.Max = bo.Initial
   144  	}
   145  	return op.wait(ctx, resp, &bo, gax.Sleep, opts...)
   146  }
   147  
   148  type sleeper func(context.Context, time.Duration) error
   149  
   150  // wait implements Wait, taking exponentialBackoff and sleeper arguments for testing.
   151  func (op *Operation) wait(ctx context.Context, resp protoadapt.MessageV1, bo *gax.Backoff, sl sleeper, opts ...gax.CallOption) error {
   152  	for {
   153  		if err := op.Poll(ctx, resp, opts...); err != nil {
   154  			return err
   155  		}
   156  		if op.Done() {
   157  			return nil
   158  		}
   159  		if err := sl(ctx, bo.Pause()); err != nil {
   160  			return err
   161  		}
   162  	}
   163  }
   164  
   165  // Cancel starts asynchronous cancellation on a long-running operation. The server
   166  // makes a best effort to cancel the operation, but success is not
   167  // guaranteed. If the server doesn't support this method, it returns
   168  // status.Code(err) == codes.Unimplemented. Clients can use
   169  // Poll or other methods to check whether the cancellation succeeded or whether the
   170  // operation completed despite cancellation. On successful cancellation,
   171  // the operation is not deleted; instead, op.Poll returns an error
   172  // with code Canceled.
   173  func (op *Operation) Cancel(ctx context.Context, opts ...gax.CallOption) error {
   174  	return op.c.CancelOperation(ctx, &pb.CancelOperationRequest{Name: op.Name()}, opts...)
   175  }
   176  
   177  // Delete deletes a long-running operation. This method indicates that the client is
   178  // no longer interested in the operation result. It does not cancel the
   179  // operation. If the server doesn't support this method, status.Code(err) == codes.Unimplemented.
   180  func (op *Operation) Delete(ctx context.Context, opts ...gax.CallOption) error {
   181  	return op.c.DeleteOperation(ctx, &pb.DeleteOperationRequest{Name: op.Name()}, opts...)
   182  }
   183  

View as plain text