Commit 420350df authored by Zhou Yaochen's avatar Zhou Yaochen
Browse files

update ignore

parent fc1f6363

Too many changes to show.

To preserve performance only 723 of 723+ files are displayed.
/*
Copyright 2016 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package bigtable
import (
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"testing"
"cloud.google.com/go/internal/testutil"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/wrappers"
btspb "google.golang.org/genproto/googleapis/bigtable/v2"
)
// Indicates that a field in the proto should be omitted, rather than included
// as a wrapped empty string.
const nilStr = "<>"
func TestSingleCell(t *testing.T) {
cr := newChunkReader()
// All in one cell
row, err := cr.Process(cc("rk", "fm", "col", 1, "value", 0, true))
if err != nil {
t.Fatalf("Processing chunk: %v", err)
}
if row == nil {
t.Fatalf("Missing row")
}
if len(row["fm"]) != 1 {
t.Fatalf("Family name length mismatch %d, %d", 1, len(row["fm"]))
}
want := []ReadItem{ri("rk", "fm", "col", 1, "value")}
if !testutil.Equal(row["fm"], want) {
t.Fatalf("Incorrect ReadItem: got: %v\nwant: %v\n", row["fm"], want)
}
if err := cr.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
}
func TestMultipleCells(t *testing.T) {
cr := newChunkReader()
mustProcess(t, cr, cc("rs", "fm1", "col1", 0, "val1", 0, false))
mustProcess(t, cr, cc("rs", "fm1", "col1", 1, "val2", 0, false))
mustProcess(t, cr, cc("rs", "fm1", "col2", 0, "val3", 0, false))
mustProcess(t, cr, cc("rs", "fm2", "col1", 0, "val4", 0, false))
row, err := cr.Process(cc("rs", "fm2", "col2", 1, "extralongval5", 0, true))
if err != nil {
t.Fatalf("Processing chunk: %v", err)
}
if row == nil {
t.Fatalf("Missing row")
}
want := []ReadItem{
ri("rs", "fm1", "col1", 0, "val1"),
ri("rs", "fm1", "col1", 1, "val2"),
ri("rs", "fm1", "col2", 0, "val3"),
}
if !testutil.Equal(row["fm1"], want) {
t.Fatalf("Incorrect ReadItem: got: %v\nwant: %v\n", row["fm1"], want)
}
want = []ReadItem{
ri("rs", "fm2", "col1", 0, "val4"),
ri("rs", "fm2", "col2", 1, "extralongval5"),
}
if !testutil.Equal(row["fm2"], want) {
t.Fatalf("Incorrect ReadItem: got: %v\nwant: %v\n", row["fm2"], want)
}
if err := cr.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
}
func TestSplitCells(t *testing.T) {
cr := newChunkReader()
mustProcess(t, cr, cc("rs", "fm1", "col1", 0, "hello ", 11, false))
mustProcess(t, cr, ccData("world", 0, false))
row, err := cr.Process(cc("rs", "fm1", "col2", 0, "val2", 0, true))
if err != nil {
t.Fatalf("Processing chunk: %v", err)
}
if row == nil {
t.Fatalf("Missing row")
}
want := []ReadItem{
ri("rs", "fm1", "col1", 0, "hello world"),
ri("rs", "fm1", "col2", 0, "val2"),
}
if !testutil.Equal(row["fm1"], want) {
t.Fatalf("Incorrect ReadItem: got: %v\nwant: %v\n", row["fm1"], want)
}
if err := cr.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
}
func TestMultipleRows(t *testing.T) {
cr := newChunkReader()
row, err := cr.Process(cc("rs1", "fm1", "col1", 1, "val1", 0, true))
if err != nil {
t.Fatalf("Processing chunk: %v", err)
}
want := []ReadItem{ri("rs1", "fm1", "col1", 1, "val1")}
if !testutil.Equal(row["fm1"], want) {
t.Fatalf("Incorrect ReadItem: got: %v\nwant: %v\n", row["fm1"], want)
}
row, err = cr.Process(cc("rs2", "fm2", "col2", 2, "val2", 0, true))
if err != nil {
t.Fatalf("Processing chunk: %v", err)
}
want = []ReadItem{ri("rs2", "fm2", "col2", 2, "val2")}
if !testutil.Equal(row["fm2"], want) {
t.Fatalf("Incorrect ReadItem: got: %v\nwant: %v\n", row["fm2"], want)
}
if err := cr.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
}
func TestBlankQualifier(t *testing.T) {
cr := newChunkReader()
row, err := cr.Process(cc("rs1", "fm1", "", 1, "val1", 0, true))
if err != nil {
t.Fatalf("Processing chunk: %v", err)
}
want := []ReadItem{ri("rs1", "fm1", "", 1, "val1")}
if !testutil.Equal(row["fm1"], want) {
t.Fatalf("Incorrect ReadItem: got: %v\nwant: %v\n", row["fm1"], want)
}
row, err = cr.Process(cc("rs2", "fm2", "col2", 2, "val2", 0, true))
if err != nil {
t.Fatalf("Processing chunk: %v", err)
}
want = []ReadItem{ri("rs2", "fm2", "col2", 2, "val2")}
if !testutil.Equal(row["fm2"], want) {
t.Fatalf("Incorrect ReadItem: got: %v\nwant: %v\n", row["fm2"], want)
}
if err := cr.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
}
func TestReset(t *testing.T) {
cr := newChunkReader()
mustProcess(t, cr, cc("rs", "fm1", "col1", 0, "val1", 0, false))
mustProcess(t, cr, cc("rs", "fm1", "col1", 1, "val2", 0, false))
mustProcess(t, cr, cc("rs", "fm1", "col2", 0, "val3", 0, false))
mustProcess(t, cr, ccReset())
row := mustProcess(t, cr, cc("rs1", "fm1", "col1", 1, "val1", 0, true))
want := []ReadItem{ri("rs1", "fm1", "col1", 1, "val1")}
if !testutil.Equal(row["fm1"], want) {
t.Fatalf("Reset: got: %v\nwant: %v\n", row["fm1"], want)
}
if err := cr.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
}
func TestNewFamEmptyQualifier(t *testing.T) {
cr := newChunkReader()
mustProcess(t, cr, cc("rs", "fm1", "col1", 0, "val1", 0, false))
_, err := cr.Process(cc(nilStr, "fm2", nilStr, 0, "val2", 0, true))
if err == nil {
t.Fatalf("Expected error on second chunk with no qualifier set")
}
}
func mustProcess(t *testing.T, cr *chunkReader, cc *btspb.ReadRowsResponse_CellChunk) Row {
row, err := cr.Process(cc)
if err != nil {
t.Fatal(err)
}
return row
}
// The read rows acceptance test reads a json file specifying a number of tests,
// each consisting of one or more cell chunk text protos and one or more resulting
// cells or errors.
type AcceptanceTest struct {
Tests []TestCase `json:"tests"`
}
type TestCase struct {
Name string `json:"name"`
Chunks []string `json:"chunks"`
Results []TestResult `json:"results"`
}
type TestResult struct {
RK string `json:"rk"`
FM string `json:"fm"`
Qual string `json:"qual"`
TS int64 `json:"ts"`
Value string `json:"value"`
Error bool `json:"error"` // If true, expect an error. Ignore any other field.
}
func TestAcceptance(t *testing.T) {
testJSON, err := ioutil.ReadFile("./testdata/read-rows-acceptance-test.json")
if err != nil {
t.Fatalf("could not open acceptance test file %v", err)
}
var accTest AcceptanceTest
err = json.Unmarshal(testJSON, &accTest)
if err != nil {
t.Fatalf("could not parse acceptance test file: %v", err)
}
for _, test := range accTest.Tests {
runTestCase(t, test)
}
}
func runTestCase(t *testing.T, test TestCase) {
// Increment an index into the result array as we get results
cr := newChunkReader()
var results []TestResult
var seenErr bool
for _, chunkText := range test.Chunks {
// Parse and pass each cell chunk to the ChunkReader
cc := &btspb.ReadRowsResponse_CellChunk{}
err := proto.UnmarshalText(chunkText, cc)
if err != nil {
t.Errorf("[%s] failed to unmarshal text proto: %s\n%s", test.Name, chunkText, err)
return
}
row, err := cr.Process(cc)
if err != nil {
results = append(results, TestResult{Error: true})
seenErr = true
break
} else {
// Turn the Row into TestResults
for fm, ris := range row {
for _, ri := range ris {
tr := TestResult{
RK: ri.Row,
FM: fm,
Qual: strings.Split(ri.Column, ":")[1],
TS: int64(ri.Timestamp),
Value: string(ri.Value),
}
results = append(results, tr)
}
}
}
}
// Only Close if we don't have an error yet, otherwise Close: is expected.
if !seenErr {
err := cr.Close()
if err != nil {
results = append(results, TestResult{Error: true})
}
}
got := toSet(results)
want := toSet(test.Results)
if !testutil.Equal(got, want) {
t.Fatalf("[%s]: got: %v\nwant: %v\n", test.Name, got, want)
}
}
func toSet(res []TestResult) map[TestResult]bool {
set := make(map[TestResult]bool)
for _, tr := range res {
set[tr] = true
}
return set
}
// ri returns a ReadItem for the given components
func ri(rk string, fm string, qual string, ts int64, val string) ReadItem {
return ReadItem{Row: rk, Column: fmt.Sprintf("%s:%s", fm, qual), Value: []byte(val), Timestamp: Timestamp(ts)}
}
// cc returns a CellChunk proto
func cc(rk string, fm string, qual string, ts int64, val string, size int32, commit bool) *btspb.ReadRowsResponse_CellChunk {
// The components of the cell key are wrapped and can be null or empty
var rkWrapper []byte
if rk == nilStr {
rkWrapper = nil
} else {
rkWrapper = []byte(rk)
}
var fmWrapper *wrappers.StringValue
if fm != nilStr {
fmWrapper = &wrappers.StringValue{Value: fm}
} else {
fmWrapper = nil
}
var qualWrapper *wrappers.BytesValue
if qual != nilStr {
qualWrapper = &wrappers.BytesValue{Value: []byte(qual)}
} else {
qualWrapper = nil
}
return &btspb.ReadRowsResponse_CellChunk{
RowKey: rkWrapper,
FamilyName: fmWrapper,
Qualifier: qualWrapper,
TimestampMicros: ts,
Value: []byte(val),
ValueSize: size,
RowStatus: &btspb.ReadRowsResponse_CellChunk_CommitRow{CommitRow: commit}}
}
// ccData returns a CellChunk with only a value and size
func ccData(val string, size int32, commit bool) *btspb.ReadRowsResponse_CellChunk {
return cc(nilStr, nilStr, nilStr, 0, val, size, commit)
}
// ccReset returns a CellChunk with RestRow set to true
func ccReset() *btspb.ReadRowsResponse_CellChunk {
return &btspb.ReadRowsResponse_CellChunk{
RowStatus: &btspb.ReadRowsResponse_CellChunk_ResetRow{ResetRow: true}}
}
/*
Copyright 2016 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package bigtable
import (
"context"
"strings"
"testing"
"time"
"cloud.google.com/go/bigtable/bttest"
"cloud.google.com/go/bigtable/internal/gax"
"cloud.google.com/go/internal/testutil"
"github.com/golang/protobuf/ptypes/wrappers"
"github.com/google/go-cmp/cmp"
"google.golang.org/api/option"
btpb "google.golang.org/genproto/googleapis/bigtable/v2"
rpcpb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func setupFakeServer(opt ...grpc.ServerOption) (tbl *Table, cleanup func(), err error) {
srv, err := bttest.NewServer("localhost:0", opt...)
if err != nil {
return nil, nil, err
}
conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
return nil, nil, err
}
client, err := NewClient(context.Background(), "client", "instance", option.WithGRPCConn(conn), option.WithGRPCDialOption(grpc.WithBlock()))
if err != nil {
return nil, nil, err
}
adminClient, err := NewAdminClient(context.Background(), "client", "instance", option.WithGRPCConn(conn), option.WithGRPCDialOption(grpc.WithBlock()))
if err != nil {
return nil, nil, err
}
if err := adminClient.CreateTable(context.Background(), "table"); err != nil {
return nil, nil, err
}
if err := adminClient.CreateColumnFamily(context.Background(), "table", "cf"); err != nil {
return nil, nil, err
}
t := client.Open("table")
cleanupFunc := func() {
adminClient.Close()
client.Close()
srv.Close()
}
return t, cleanupFunc, nil
}
func TestRetryApply(t *testing.T) {
gax.Logger = nil
ctx := context.Background()
errCount := 0
code := codes.Unavailable // Will be retried
// Intercept requests and return an error or defer to the underlying handler
errInjector := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if strings.HasSuffix(info.FullMethod, "MutateRow") && errCount < 3 {
errCount++
return nil, status.Errorf(code, "")
}
return handler(ctx, req)
}
tbl, cleanup, err := setupFakeServer(grpc.UnaryInterceptor(errInjector))
if err != nil {
t.Fatalf("fake server setup: %v", err)
}
defer cleanup()
mut := NewMutation()
mut.Set("cf", "col", 1000, []byte("val"))
if err := tbl.Apply(ctx, "row1", mut); err != nil {
t.Errorf("applying single mutation with retries: %v", err)
}
row, err := tbl.ReadRow(ctx, "row1")
if err != nil {
t.Errorf("reading single value with retries: %v", err)
}
if row == nil {
t.Errorf("applying single mutation with retries: could not read back row")
}
code = codes.FailedPrecondition // Won't be retried
errCount = 0
if err := tbl.Apply(ctx, "row", mut); err == nil {
t.Errorf("applying single mutation with no retries: no error")
}
// Check and mutate
mutTrue := NewMutation()
mutTrue.DeleteRow()
mutFalse := NewMutation()
mutFalse.Set("cf", "col", 1000, []byte("val"))
condMut := NewCondMutation(ValueFilter(".*"), mutTrue, mutFalse)
errCount = 0
code = codes.Unavailable // Will be retried
if err := tbl.Apply(ctx, "row1", condMut); err != nil {
t.Errorf("conditionally mutating row with retries: %v", err)
}
row, err = tbl.ReadRow(ctx, "row1") // row1 already in the table
if err != nil {
t.Errorf("reading single value after conditional mutation: %v", err)
}
if row != nil {
t.Errorf("reading single value after conditional mutation: row not deleted")
}
errCount = 0
code = codes.FailedPrecondition // Won't be retried
if err := tbl.Apply(ctx, "row", condMut); err == nil {
t.Errorf("conditionally mutating row with no retries: no error")
}
}
func TestRetryApplyBulk(t *testing.T) {
ctx := context.Background()
gax.Logger = nil
// Intercept requests and delegate to an interceptor defined by the test case
errCount := 0
var f func(grpc.ServerStream) error
errInjector := func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
if strings.HasSuffix(info.FullMethod, "MutateRows") {
return f(ss)
}
return handler(ctx, ss)
}
tbl, cleanup, err := setupFakeServer(grpc.StreamInterceptor(errInjector))
defer cleanup()
if err != nil {
t.Fatalf("fake server setup: %v", err)
}
errCount = 0
// Test overall request failure and retries
f = func(ss grpc.ServerStream) error {
if errCount < 3 {
errCount++
return status.Errorf(codes.Aborted, "")
}
return nil
}
mut := NewMutation()
mut.Set("cf", "col", 1, []byte{})
errors, err := tbl.ApplyBulk(ctx, []string{"row2"}, []*Mutation{mut})
if errors != nil || err != nil {
t.Errorf("bulk with request failure: got: %v, %v, want: nil", errors, err)
}
// Test failures and retries in one request
errCount = 0
m1 := NewMutation()
m1.Set("cf", "col", 1, []byte{})
m2 := NewMutation()
m2.Set("cf", "col2", 1, []byte{})
m3 := NewMutation()
m3.Set("cf", "col3", 1, []byte{})
f = func(ss grpc.ServerStream) error {
var err error
req := new(btpb.MutateRowsRequest)
must(ss.RecvMsg(req))
switch errCount {
case 0:
// Retryable request failure
err = status.Errorf(codes.Unavailable, "")
case 1:
// Two mutations fail
must(writeMutateRowsResponse(ss, codes.Unavailable, codes.OK, codes.Aborted))
err = nil
case 2:
// Two failures were retried. One will succeed.
if want, got := 2, len(req.Entries); want != got {
t.Errorf("2 bulk retries, got: %d, want %d", got, want)
}
must(writeMutateRowsResponse(ss, codes.OK, codes.Aborted))
err = nil
case 3:
// One failure was retried and will succeed.
if want, got := 1, len(req.Entries); want != got {
t.Errorf("1 bulk retry, got: %d, want %d", got, want)
}
must(writeMutateRowsResponse(ss, codes.OK))
err = nil
}
errCount++
return err
}
errors, err = tbl.ApplyBulk(ctx, []string{"row1", "row2", "row3"}, []*Mutation{m1, m2, m3})
if errors != nil || err != nil {
t.Errorf("bulk with retries: got: %v, %v, want: nil", errors, err)
}
// Test unretryable errors
niMut := NewMutation()
niMut.Set("cf", "col", ServerTime, []byte{}) // Non-idempotent
errCount = 0
f = func(ss grpc.ServerStream) error {
var err error
req := new(btpb.MutateRowsRequest)
must(ss.RecvMsg(req))
switch errCount {
case 0:
// Give non-idempotent mutation a retryable error code.
// Nothing should be retried.
must(writeMutateRowsResponse(ss, codes.FailedPrecondition, codes.Aborted))
err = nil
case 1:
t.Errorf("unretryable errors: got one retry, want no retries")
}
errCount++
return err
}
errors, err = tbl.ApplyBulk(ctx, []string{"row1", "row2"}, []*Mutation{m1, niMut})
if err != nil {
t.Errorf("unretryable errors: request failed %v", err)
}
want := []error{
status.Errorf(codes.FailedPrecondition, ""),
status.Errorf(codes.Aborted, ""),
}
if !testutil.Equal(want, errors) {
t.Errorf("unretryable errors: got: %v, want: %v", errors, want)
}
// Test individual errors and a deadline exceeded
f = func(ss grpc.ServerStream) error {
return writeMutateRowsResponse(ss, codes.FailedPrecondition, codes.OK, codes.Aborted)
}
ctx, _ = context.WithTimeout(ctx, 100*time.Millisecond)
errors, err = tbl.ApplyBulk(ctx, []string{"row1", "row2", "row3"}, []*Mutation{m1, m2, m3})
wantErr := context.DeadlineExceeded
if wantErr != err {
t.Errorf("deadline exceeded error: got: %v, want: %v", err, wantErr)
}
if errors != nil {
t.Errorf("deadline exceeded errors: got: %v, want: nil", err)
}
}
func writeMutateRowsResponse(ss grpc.ServerStream, codes ...codes.Code) error {
res := &btpb.MutateRowsResponse{Entries: make([]*btpb.MutateRowsResponse_Entry, len(codes))}
for i, code := range codes {
res.Entries[i] = &btpb.MutateRowsResponse_Entry{
Index: int64(i),
Status: &rpcpb.Status{Code: int32(code), Message: ""},
}
}
return ss.SendMsg(res)
}
func TestRetainRowsAfter(t *testing.T) {
prevRowRange := NewRange("a", "z")
prevRowKey := "m"
want := NewRange("m\x00", "z")
got := prevRowRange.retainRowsAfter(prevRowKey)
if !testutil.Equal(want, got, cmp.AllowUnexported(RowRange{})) {
t.Errorf("range retry: got %v, want %v", got, want)
}
prevRowRangeList := RowRangeList{NewRange("a", "d"), NewRange("e", "g"), NewRange("h", "l")}
prevRowKey = "f"
wantRowRangeList := RowRangeList{NewRange("f\x00", "g"), NewRange("h", "l")}
got = prevRowRangeList.retainRowsAfter(prevRowKey)
if !testutil.Equal(wantRowRangeList, got, cmp.AllowUnexported(RowRange{})) {
t.Errorf("range list retry: got %v, want %v", got, wantRowRangeList)
}
prevRowList := RowList{"a", "b", "c", "d", "e", "f"}
prevRowKey = "b"
wantList := RowList{"c", "d", "e", "f"}
got = prevRowList.retainRowsAfter(prevRowKey)
if !testutil.Equal(wantList, got) {
t.Errorf("list retry: got %v, want %v", got, wantList)
}
}
func TestRetryReadRows(t *testing.T) {
ctx := context.Background()
gax.Logger = nil
// Intercept requests and delegate to an interceptor defined by the test case
errCount := 0
var f func(grpc.ServerStream) error
errInjector := func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
if strings.HasSuffix(info.FullMethod, "ReadRows") {
return f(ss)
}
return handler(ctx, ss)
}
tbl, cleanup, err := setupFakeServer(grpc.StreamInterceptor(errInjector))
defer cleanup()
if err != nil {
t.Fatalf("fake server setup: %v", err)
}
errCount = 0
// Test overall request failure and retries
f = func(ss grpc.ServerStream) error {
var err error
req := new(btpb.ReadRowsRequest)
must(ss.RecvMsg(req))
switch errCount {
case 0:
// Retryable request failure
err = status.Errorf(codes.Unavailable, "")
case 1:
// Write two rows then error
if want, got := "a", string(req.Rows.RowRanges[0].GetStartKeyClosed()); want != got {
t.Errorf("first retry, no data received yet: got %q, want %q", got, want)
}
must(writeReadRowsResponse(ss, "a", "b"))
err = status.Errorf(codes.Unavailable, "")
case 2:
// Retryable request failure
if want, got := "b\x00", string(req.Rows.RowRanges[0].GetStartKeyClosed()); want != got {
t.Errorf("2 range retries: got %q, want %q", got, want)
}
err = status.Errorf(codes.Unavailable, "")
case 3:
// Write two more rows
must(writeReadRowsResponse(ss, "c", "d"))
err = nil
}
errCount++
return err
}
var got []string
must(tbl.ReadRows(ctx, NewRange("a", "z"), func(r Row) bool {
got = append(got, r.Key())
return true
}))
want := []string{"a", "b", "c", "d"}
if !testutil.Equal(got, want) {
t.Errorf("retry range integration: got %v, want %v", got, want)
}
}
func writeReadRowsResponse(ss grpc.ServerStream, rowKeys ...string) error {
var chunks []*btpb.ReadRowsResponse_CellChunk
for _, key := range rowKeys {
chunks = append(chunks, &btpb.ReadRowsResponse_CellChunk{
RowKey: []byte(key),
FamilyName: &wrappers.StringValue{Value: "fm"},
Qualifier: &wrappers.BytesValue{Value: []byte("col")},
RowStatus: &btpb.ReadRowsResponse_CellChunk_CommitRow{CommitRow: true},
})
}
return ss.SendMsg(&btpb.ReadRowsResponse{Chunks: chunks})
}
func must(err error) {
if err != nil {
panic(err)
}
}
{
"tests": [
{
"name": "invalid - no commit",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "invalid - no cell key before commit",
"chunks": [
"commit_row: true\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "invalid - no cell key before value",
"chunks": [
"timestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "invalid - new col family must specify qualifier",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"family_name: \u003c\n value: \"B\"\n\u003e\ntimestamp_micros: 102\nvalue: \"value-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "bare commit implies ts=0",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n",
"commit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 0,
"value": "",
"label": "",
"error": false
}
]
},
{
"name": "simple row with timestamp",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
}
]
},
{
"name": "missing timestamp, implied ts=0",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\nvalue: \"value-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 0,
"value": "value-VAL",
"label": "",
"error": false
}
]
},
{
"name": "empty cell value",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 0,
"value": "",
"label": "",
"error": false
}
]
},
{
"name": "two unsplit cells",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"timestamp_micros: 102\nvalue: \"value-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "two qualifiers",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"qualifier: \u003c\n value: \"D\"\n\u003e\ntimestamp_micros: 102\nvalue: \"value-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "D",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "two families",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"family_name: \u003c\n value: \"B\"\n\u003e\nqualifier: \u003c\n value: \"E\"\n\u003e\ntimestamp_micros: 102\nvalue: \"value-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "B",
"qual": "E",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "with labels",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nlabels: \"L_1\"\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"timestamp_micros: 102\nlabels: \"L_2\"\nvalue: \"value-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "L_1",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 102,
"value": "value-VAL_2",
"label": "L_2",
"error": false
}
]
},
{
"name": "split cell, bare commit",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"alue-VAL\"\ncommit_row: false\n",
"commit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 0,
"value": "",
"label": "",
"error": false
}
]
},
{
"name": "split cell",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"alue-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
}
]
},
{
"name": "split four ways",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nlabels: \"L\"\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"a\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"l\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"ue-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "L",
"error": false
}
]
},
{
"name": "two split cells",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"alue-VAL_1\"\ncommit_row: false\n",
"timestamp_micros: 102\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"alue-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "multi-qualifier splits",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"alue-VAL_1\"\ncommit_row: false\n",
"qualifier: \u003c\n value: \"D\"\n\u003e\ntimestamp_micros: 102\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"alue-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "D",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "multi-qualifier multi-split",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"a\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"lue-VAL_1\"\ncommit_row: false\n",
"qualifier: \u003c\n value: \"D\"\n\u003e\ntimestamp_micros: 102\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"a\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"lue-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "D",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "multi-family split",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"alue-VAL_1\"\ncommit_row: false\n",
"family_name: \u003c\n value: \"B\"\n\u003e\nqualifier: \u003c\n value: \"E\"\n\u003e\ntimestamp_micros: 102\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"alue-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "B",
"qual": "E",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "invalid - no commit between rows",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "invalid - no commit after first row",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "invalid - last row missing commit",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
},
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "invalid - duplicate row key",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n",
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"B\"\n\u003e\nqualifier: \u003c\n value: \"D\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
},
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "invalid - new row missing row key",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n",
"timestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
},
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "two rows",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
},
{
"rk": "RK_2",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
}
]
},
{
"name": "two rows implicit timestamp",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\nvalue: \"value-VAL\"\ncommit_row: true\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 0,
"value": "value-VAL",
"label": "",
"error": false
},
{
"rk": "RK_2",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
}
]
},
{
"name": "two rows empty value",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ncommit_row: true\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 0,
"value": "",
"label": "",
"error": false
},
{
"rk": "RK_2",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
}
]
},
{
"name": "two rows, one with multiple cells",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"timestamp_micros: 102\nvalue: \"value-VAL_2\"\ncommit_row: true\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"B\"\n\u003e\nqualifier: \u003c\n value: \"D\"\n\u003e\ntimestamp_micros: 103\nvalue: \"value-VAL_3\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
},
{
"rk": "RK_2",
"fm": "B",
"qual": "D",
"ts": 103,
"value": "value-VAL_3",
"label": "",
"error": false
}
]
},
{
"name": "two rows, multiple cells",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"qualifier: \u003c\n value: \"D\"\n\u003e\ntimestamp_micros: 102\nvalue: \"value-VAL_2\"\ncommit_row: true\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"B\"\n\u003e\nqualifier: \u003c\n value: \"E\"\n\u003e\ntimestamp_micros: 103\nvalue: \"value-VAL_3\"\ncommit_row: false\n",
"qualifier: \u003c\n value: \"F\"\n\u003e\ntimestamp_micros: 104\nvalue: \"value-VAL_4\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK_1",
"fm": "A",
"qual": "D",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
},
{
"rk": "RK_2",
"fm": "B",
"qual": "E",
"ts": 103,
"value": "value-VAL_3",
"label": "",
"error": false
},
{
"rk": "RK_2",
"fm": "B",
"qual": "F",
"ts": 104,
"value": "value-VAL_4",
"label": "",
"error": false
}
]
},
{
"name": "two rows, multiple cells, multiple families",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"family_name: \u003c\n value: \"B\"\n\u003e\nqualifier: \u003c\n value: \"E\"\n\u003e\ntimestamp_micros: 102\nvalue: \"value-VAL_2\"\ncommit_row: true\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"M\"\n\u003e\nqualifier: \u003c\n value: \"O\"\n\u003e\ntimestamp_micros: 103\nvalue: \"value-VAL_3\"\ncommit_row: false\n",
"family_name: \u003c\n value: \"N\"\n\u003e\nqualifier: \u003c\n value: \"P\"\n\u003e\ntimestamp_micros: 104\nvalue: \"value-VAL_4\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK_1",
"fm": "B",
"qual": "E",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
},
{
"rk": "RK_2",
"fm": "M",
"qual": "O",
"ts": 103,
"value": "value-VAL_3",
"label": "",
"error": false
},
{
"rk": "RK_2",
"fm": "N",
"qual": "P",
"ts": 104,
"value": "value-VAL_4",
"label": "",
"error": false
}
]
},
{
"name": "two rows, four cells, 2 labels",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 101\nlabels: \"L_1\"\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"timestamp_micros: 102\nvalue: \"value-VAL_2\"\ncommit_row: true\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"B\"\n\u003e\nqualifier: \u003c\n value: \"D\"\n\u003e\ntimestamp_micros: 103\nlabels: \"L_3\"\nvalue: \"value-VAL_3\"\ncommit_row: false\n",
"timestamp_micros: 104\nvalue: \"value-VAL_4\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 101,
"value": "value-VAL_1",
"label": "L_1",
"error": false
},
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 102,
"value": "value-VAL_2",
"label": "",
"error": false
},
{
"rk": "RK_2",
"fm": "B",
"qual": "D",
"ts": 103,
"value": "value-VAL_3",
"label": "L_3",
"error": false
},
{
"rk": "RK_2",
"fm": "B",
"qual": "D",
"ts": 104,
"value": "value-VAL_4",
"label": "",
"error": false
}
]
},
{
"name": "two rows with splits, same timestamp",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"alue-VAL_1\"\ncommit_row: true\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"alue-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL_1",
"label": "",
"error": false
},
{
"rk": "RK_2",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "invalid - bare reset",
"chunks": [
"reset_row: true\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "invalid - bad reset, no commit",
"chunks": [
"reset_row: true\n",
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "invalid - missing key after reset",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n",
"reset_row: true\n",
"timestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "no data after reset",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n",
"reset_row: true\n"
],
"results": null
},
{
"name": "simple reset",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n",
"reset_row: true\n",
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
}
]
},
{
"name": "reset to new val",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"reset_row: true\n",
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "reset to new qual",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"reset_row: true\n",
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"D\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_1\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "D",
"ts": 100,
"value": "value-VAL_1",
"label": "",
"error": false
}
]
},
{
"name": "reset with splits",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"timestamp_micros: 102\nvalue: \"value-VAL_2\"\ncommit_row: false\n",
"reset_row: true\n",
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "reset two cells",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"reset_row: true\n",
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_2\"\ncommit_row: false\n",
"timestamp_micros: 103\nvalue: \"value-VAL_3\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL_2",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 103,
"value": "value-VAL_3",
"label": "",
"error": false
}
]
},
{
"name": "two resets",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"reset_row: true\n",
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_2\"\ncommit_row: false\n",
"reset_row: true\n",
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_3\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL_3",
"label": "",
"error": false
}
]
},
{
"name": "reset then two cells",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"reset_row: true\n",
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"B\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_2\"\ncommit_row: false\n",
"qualifier: \u003c\n value: \"D\"\n\u003e\ntimestamp_micros: 103\nvalue: \"value-VAL_3\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "B",
"qual": "C",
"ts": 100,
"value": "value-VAL_2",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "B",
"qual": "D",
"ts": 103,
"value": "value-VAL_3",
"label": "",
"error": false
}
]
},
{
"name": "reset to new row",
"chunks": [
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_1\"\ncommit_row: false\n",
"reset_row: true\n",
"row_key: \"RK_2\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_2\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_2",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL_2",
"label": "",
"error": false
}
]
},
{
"name": "reset in between chunks",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nlabels: \"L\"\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"a\"\nvalue_size: 10\ncommit_row: false\n",
"reset_row: true\n",
"row_key: \"RK_1\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL_1\"\ncommit_row: true\n"
],
"results": [
{
"rk": "RK_1",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL_1",
"label": "",
"error": false
}
]
},
{
"name": "invalid - reset with chunk",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nlabels: \"L\"\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"a\"\nvalue_size: 10\nreset_row: true\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "invalid - commit with chunk",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nlabels: \"L\"\nvalue: \"v\"\nvalue_size: 10\ncommit_row: false\n",
"value: \"a\"\nvalue_size: 10\ncommit_row: true\n"
],
"results": [
{
"rk": "",
"fm": "",
"qual": "",
"ts": 0,
"value": "",
"label": "",
"error": true
}
]
},
{
"name": "empty cell chunk",
"chunks": [
"row_key: \"RK\"\nfamily_name: \u003c\n value: \"A\"\n\u003e\nqualifier: \u003c\n value: \"C\"\n\u003e\ntimestamp_micros: 100\nvalue: \"value-VAL\"\ncommit_row: false\n",
"commit_row: false\n",
"commit_row: true\n"
],
"results": [
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 100,
"value": "value-VAL",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 0,
"value": "",
"label": "",
"error": false
},
{
"rk": "RK",
"fm": "A",
"qual": "C",
"ts": 0,
"value": "",
"label": "",
"error": false
}
]
}
]
}
\ No newline at end of file
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bigtable
import (
"context"
"fmt"
"go.opencensus.io/trace"
)
func traceStartSpan(ctx context.Context, name string) context.Context {
ctx, _ = trace.StartSpan(ctx, name)
return ctx
}
func traceEndSpan(ctx context.Context, err error) {
span := trace.FromContext(ctx)
if err != nil {
span.SetStatus(trace.Status{Message: err.Error()})
}
span.End()
}
func tracePrintf(ctx context.Context, attrMap map[string]interface{}, format string, args ...interface{}) {
var attrs []trace.Attribute
for k, v := range attrMap {
var a trace.Attribute
switch v := v.(type) {
case string:
a = trace.StringAttribute(k, v)
case bool:
a = trace.BoolAttribute(k, v)
case int:
a = trace.Int64Attribute(k, int64(v))
case int64:
a = trace.Int64Attribute(k, v)
default:
a = trace.StringAttribute(k, fmt.Sprintf("%#v", v))
}
attrs = append(attrs, a)
}
trace.FromContext(ctx).Annotatef(attrs, format, args...)
}
// Copyright 2016 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package civil implements types for civil time, a time-zone-independent
// representation of time that follows the rules of the proleptic
// Gregorian calendar with exactly 24-hour days, 60-minute hours, and 60-second
// minutes.
//
// Because they lack location information, these types do not represent unique
// moments or intervals of time. Use time.Time for that purpose.
package civil
import (
"fmt"
"time"
)
// A Date represents a date (year, month, day).
//
// This type does not include location information, and therefore does not
// describe a unique 24-hour timespan.
type Date struct {
Year int // Year (e.g., 2014).
Month time.Month // Month of the year (January = 1, ...).
Day int // Day of the month, starting at 1.
}
// DateOf returns the Date in which a time occurs in that time's location.
func DateOf(t time.Time) Date {
var d Date
d.Year, d.Month, d.Day = t.Date()
return d
}
// ParseDate parses a string in RFC3339 full-date format and returns the date value it represents.
func ParseDate(s string) (Date, error) {
t, err := time.Parse("2006-01-02", s)
if err != nil {
return Date{}, err
}
return DateOf(t), nil
}
// String returns the date in RFC3339 full-date format.
func (d Date) String() string {
return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
}
// IsValid reports whether the date is valid.
func (d Date) IsValid() bool {
return DateOf(d.In(time.UTC)) == d
}
// In returns the time corresponding to time 00:00:00 of the date in the location.
//
// In is always consistent with time.Date, even when time.Date returns a time
// on a different day. For example, if loc is America/Indiana/Vincennes, then both
// time.Date(1955, time.May, 1, 0, 0, 0, 0, loc)
// and
// civil.Date{Year: 1955, Month: time.May, Day: 1}.In(loc)
// return 23:00:00 on April 30, 1955.
//
// In panics if loc is nil.
func (d Date) In(loc *time.Location) time.Time {
return time.Date(d.Year, d.Month, d.Day, 0, 0, 0, 0, loc)
}
// AddDays returns the date that is n days in the future.
// n can also be negative to go into the past.
func (d Date) AddDays(n int) Date {
return DateOf(d.In(time.UTC).AddDate(0, 0, n))
}
// DaysSince returns the signed number of days between the date and s, not including the end day.
// This is the inverse operation to AddDays.
func (d Date) DaysSince(s Date) (days int) {
// We convert to Unix time so we do not have to worry about leap seconds:
// Unix time increases by exactly 86400 seconds per day.
deltaUnix := d.In(time.UTC).Unix() - s.In(time.UTC).Unix()
return int(deltaUnix / 86400)
}
// Before reports whether d1 occurs before d2.
func (d1 Date) Before(d2 Date) bool {
if d1.Year != d2.Year {
return d1.Year < d2.Year
}
if d1.Month != d2.Month {
return d1.Month < d2.Month
}
return d1.Day < d2.Day
}
// After reports whether d1 occurs after d2.
func (d1 Date) After(d2 Date) bool {
return d2.Before(d1)
}
// MarshalText implements the encoding.TextMarshaler interface.
// The output is the result of d.String().
func (d Date) MarshalText() ([]byte, error) {
return []byte(d.String()), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// The date is expected to be a string in a format accepted by ParseDate.
func (d *Date) UnmarshalText(data []byte) error {
var err error
*d, err = ParseDate(string(data))
return err
}
// A Time represents a time with nanosecond precision.
//
// This type does not include location information, and therefore does not
// describe a unique moment in time.
//
// This type exists to represent the TIME type in storage-based APIs like BigQuery.
// Most operations on Times are unlikely to be meaningful. Prefer the DateTime type.
type Time struct {
Hour int // The hour of the day in 24-hour format; range [0-23]
Minute int // The minute of the hour; range [0-59]
Second int // The second of the minute; range [0-59]
Nanosecond int // The nanosecond of the second; range [0-999999999]
}
// TimeOf returns the Time representing the time of day in which a time occurs
// in that time's location. It ignores the date.
func TimeOf(t time.Time) Time {
var tm Time
tm.Hour, tm.Minute, tm.Second = t.Clock()
tm.Nanosecond = t.Nanosecond()
return tm
}
// ParseTime parses a string and returns the time value it represents.
// ParseTime accepts an extended form of the RFC3339 partial-time format. After
// the HH:MM:SS part of the string, an optional fractional part may appear,
// consisting of a decimal point followed by one to nine decimal digits.
// (RFC3339 admits only one digit after the decimal point).
func ParseTime(s string) (Time, error) {
t, err := time.Parse("15:04:05.999999999", s)
if err != nil {
return Time{}, err
}
return TimeOf(t), nil
}
// String returns the date in the format described in ParseTime. If Nanoseconds
// is zero, no fractional part will be generated. Otherwise, the result will
// end with a fractional part consisting of a decimal point and nine digits.
func (t Time) String() string {
s := fmt.Sprintf("%02d:%02d:%02d", t.Hour, t.Minute, t.Second)
if t.Nanosecond == 0 {
return s
}
return s + fmt.Sprintf(".%09d", t.Nanosecond)
}
// IsValid reports whether the time is valid.
func (t Time) IsValid() bool {
// Construct a non-zero time.
tm := time.Date(2, 2, 2, t.Hour, t.Minute, t.Second, t.Nanosecond, time.UTC)
return TimeOf(tm) == t
}
// MarshalText implements the encoding.TextMarshaler interface.
// The output is the result of t.String().
func (t Time) MarshalText() ([]byte, error) {
return []byte(t.String()), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// The time is expected to be a string in a format accepted by ParseTime.
func (t *Time) UnmarshalText(data []byte) error {
var err error
*t, err = ParseTime(string(data))
return err
}
// A DateTime represents a date and time.
//
// This type does not include location information, and therefore does not
// describe a unique moment in time.
type DateTime struct {
Date Date
Time Time
}
// Note: We deliberately do not embed Date into DateTime, to avoid promoting AddDays and Sub.
// DateTimeOf returns the DateTime in which a time occurs in that time's location.
func DateTimeOf(t time.Time) DateTime {
return DateTime{
Date: DateOf(t),
Time: TimeOf(t),
}
}
// ParseDateTime parses a string and returns the DateTime it represents.
// ParseDateTime accepts a variant of the RFC3339 date-time format that omits
// the time offset but includes an optional fractional time, as described in
// ParseTime. Informally, the accepted format is
// YYYY-MM-DDTHH:MM:SS[.FFFFFFFFF]
// where the 'T' may be a lower-case 't'.
func ParseDateTime(s string) (DateTime, error) {
t, err := time.Parse("2006-01-02T15:04:05.999999999", s)
if err != nil {
t, err = time.Parse("2006-01-02t15:04:05.999999999", s)
if err != nil {
return DateTime{}, err
}
}
return DateTimeOf(t), nil
}
// String returns the date in the format described in ParseDate.
func (dt DateTime) String() string {
return dt.Date.String() + "T" + dt.Time.String()
}
// IsValid reports whether the datetime is valid.
func (dt DateTime) IsValid() bool {
return dt.Date.IsValid() && dt.Time.IsValid()
}
// In returns the time corresponding to the DateTime in the given location.
//
// If the time is missing or ambigous at the location, In returns the same
// result as time.Date. For example, if loc is America/Indiana/Vincennes, then
// both
// time.Date(1955, time.May, 1, 0, 30, 0, 0, loc)
// and
// civil.DateTime{
// civil.Date{Year: 1955, Month: time.May, Day: 1}},
// civil.Time{Minute: 30}}.In(loc)
// return 23:30:00 on April 30, 1955.
//
// In panics if loc is nil.
func (dt DateTime) In(loc *time.Location) time.Time {
return time.Date(dt.Date.Year, dt.Date.Month, dt.Date.Day, dt.Time.Hour, dt.Time.Minute, dt.Time.Second, dt.Time.Nanosecond, loc)
}
// Before reports whether dt1 occurs before dt2.
func (dt1 DateTime) Before(dt2 DateTime) bool {
return dt1.In(time.UTC).Before(dt2.In(time.UTC))
}
// After reports whether dt1 occurs after dt2.
func (dt1 DateTime) After(dt2 DateTime) bool {
return dt2.Before(dt1)
}
// MarshalText implements the encoding.TextMarshaler interface.
// The output is the result of dt.String().
func (dt DateTime) MarshalText() ([]byte, error) {
return []byte(dt.String()), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// The datetime is expected to be a string in a format accepted by ParseDateTime
func (dt *DateTime) UnmarshalText(data []byte) error {
var err error
*dt, err = ParseDateTime(string(data))
return err
}
// Copyright 2016 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package civil
import (
"encoding/json"
"testing"
"time"
"github.com/google/go-cmp/cmp"
)
func TestDates(t *testing.T) {
for _, test := range []struct {
date Date
loc *time.Location
wantStr string
wantTime time.Time
}{
{
date: Date{2014, 7, 29},
loc: time.Local,
wantStr: "2014-07-29",
wantTime: time.Date(2014, time.July, 29, 0, 0, 0, 0, time.Local),
},
{
date: DateOf(time.Date(2014, 8, 20, 15, 8, 43, 1, time.Local)),
loc: time.UTC,
wantStr: "2014-08-20",
wantTime: time.Date(2014, 8, 20, 0, 0, 0, 0, time.UTC),
},
{
date: DateOf(time.Date(999, time.January, 26, 0, 0, 0, 0, time.Local)),
loc: time.UTC,
wantStr: "0999-01-26",
wantTime: time.Date(999, 1, 26, 0, 0, 0, 0, time.UTC),
},
} {
if got := test.date.String(); got != test.wantStr {
t.Errorf("%#v.String() = %q, want %q", test.date, got, test.wantStr)
}
if got := test.date.In(test.loc); !got.Equal(test.wantTime) {
t.Errorf("%#v.In(%v) = %v, want %v", test.date, test.loc, got, test.wantTime)
}
}
}
func TestDateIsValid(t *testing.T) {
for _, test := range []struct {
date Date
want bool
}{
{Date{2014, 7, 29}, true},
{Date{2000, 2, 29}, true},
{Date{10000, 12, 31}, true},
{Date{1, 1, 1}, true},
{Date{0, 1, 1}, true}, // year zero is OK
{Date{-1, 1, 1}, true}, // negative year is OK
{Date{1, 0, 1}, false},
{Date{1, 1, 0}, false},
{Date{2016, 1, 32}, false},
{Date{2016, 13, 1}, false},
{Date{1, -1, 1}, false},
{Date{1, 1, -1}, false},
} {
got := test.date.IsValid()
if got != test.want {
t.Errorf("%#v: got %t, want %t", test.date, got, test.want)
}
}
}
func TestParseDate(t *testing.T) {
for _, test := range []struct {
str string
want Date // if empty, expect an error
}{
{"2016-01-02", Date{2016, 1, 2}},
{"2016-12-31", Date{2016, 12, 31}},
{"0003-02-04", Date{3, 2, 4}},
{"999-01-26", Date{}},
{"", Date{}},
{"2016-01-02x", Date{}},
} {
got, err := ParseDate(test.str)
if got != test.want {
t.Errorf("ParseDate(%q) = %+v, want %+v", test.str, got, test.want)
}
if err != nil && test.want != (Date{}) {
t.Errorf("Unexpected error %v from ParseDate(%q)", err, test.str)
}
}
}
func TestDateArithmetic(t *testing.T) {
for _, test := range []struct {
desc string
start Date
end Date
days int
}{
{
desc: "zero days noop",
start: Date{2014, 5, 9},
end: Date{2014, 5, 9},
days: 0,
},
{
desc: "crossing a year boundary",
start: Date{2014, 12, 31},
end: Date{2015, 1, 1},
days: 1,
},
{
desc: "negative number of days",
start: Date{2015, 1, 1},
end: Date{2014, 12, 31},
days: -1,
},
{
desc: "full leap year",
start: Date{2004, 1, 1},
end: Date{2005, 1, 1},
days: 366,
},
{
desc: "full non-leap year",
start: Date{2001, 1, 1},
end: Date{2002, 1, 1},
days: 365,
},
{
desc: "crossing a leap second",
start: Date{1972, 6, 30},
end: Date{1972, 7, 1},
days: 1,
},
{
desc: "dates before the unix epoch",
start: Date{101, 1, 1},
end: Date{102, 1, 1},
days: 365,
},
} {
if got := test.start.AddDays(test.days); got != test.end {
t.Errorf("[%s] %#v.AddDays(%v) = %#v, want %#v", test.desc, test.start, test.days, got, test.end)
}
if got := test.end.DaysSince(test.start); got != test.days {
t.Errorf("[%s] %#v.Sub(%#v) = %v, want %v", test.desc, test.end, test.start, got, test.days)
}
}
}
func TestDateBefore(t *testing.T) {
for _, test := range []struct {
d1, d2 Date
want bool
}{
{Date{2016, 12, 31}, Date{2017, 1, 1}, true},
{Date{2016, 1, 1}, Date{2016, 1, 1}, false},
{Date{2016, 12, 30}, Date{2016, 12, 31}, true},
} {
if got := test.d1.Before(test.d2); got != test.want {
t.Errorf("%v.Before(%v): got %t, want %t", test.d1, test.d2, got, test.want)
}
}
}
func TestDateAfter(t *testing.T) {
for _, test := range []struct {
d1, d2 Date
want bool
}{
{Date{2016, 12, 31}, Date{2017, 1, 1}, false},
{Date{2016, 1, 1}, Date{2016, 1, 1}, false},
{Date{2016, 12, 30}, Date{2016, 12, 31}, false},
} {
if got := test.d1.After(test.d2); got != test.want {
t.Errorf("%v.After(%v): got %t, want %t", test.d1, test.d2, got, test.want)
}
}
}
func TestTimeToString(t *testing.T) {
for _, test := range []struct {
str string
time Time
roundTrip bool // ParseTime(str).String() == str?
}{
{"13:26:33", Time{13, 26, 33, 0}, true},
{"01:02:03.000023456", Time{1, 2, 3, 23456}, true},
{"00:00:00.000000001", Time{0, 0, 0, 1}, true},
{"13:26:03.1", Time{13, 26, 3, 100000000}, false},
{"13:26:33.0000003", Time{13, 26, 33, 300}, false},
} {
gotTime, err := ParseTime(test.str)
if err != nil {
t.Errorf("ParseTime(%q): got error: %v", test.str, err)
continue
}
if gotTime != test.time {
t.Errorf("ParseTime(%q) = %+v, want %+v", test.str, gotTime, test.time)
}
if test.roundTrip {
gotStr := test.time.String()
if gotStr != test.str {
t.Errorf("%#v.String() = %q, want %q", test.time, gotStr, test.str)
}
}
}
}
func TestTimeOf(t *testing.T) {
for _, test := range []struct {
time time.Time
want Time
}{
{time.Date(2014, 8, 20, 15, 8, 43, 1, time.Local), Time{15, 8, 43, 1}},
{time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), Time{0, 0, 0, 0}},
} {
if got := TimeOf(test.time); got != test.want {
t.Errorf("TimeOf(%v) = %+v, want %+v", test.time, got, test.want)
}
}
}
func TestTimeIsValid(t *testing.T) {
for _, test := range []struct {
time Time
want bool
}{
{Time{0, 0, 0, 0}, true},
{Time{23, 0, 0, 0}, true},
{Time{23, 59, 59, 999999999}, true},
{Time{24, 59, 59, 999999999}, false},
{Time{23, 60, 59, 999999999}, false},
{Time{23, 59, 60, 999999999}, false},
{Time{23, 59, 59, 1000000000}, false},
{Time{-1, 0, 0, 0}, false},
{Time{0, -1, 0, 0}, false},
{Time{0, 0, -1, 0}, false},
{Time{0, 0, 0, -1}, false},
} {
got := test.time.IsValid()
if got != test.want {
t.Errorf("%#v: got %t, want %t", test.time, got, test.want)
}
}
}
func TestDateTimeToString(t *testing.T) {
for _, test := range []struct {
str string
dateTime DateTime
roundTrip bool // ParseDateTime(str).String() == str?
}{
{"2016-03-22T13:26:33", DateTime{Date{2016, 03, 22}, Time{13, 26, 33, 0}}, true},
{"2016-03-22T13:26:33.000000600", DateTime{Date{2016, 03, 22}, Time{13, 26, 33, 600}}, true},
{"2016-03-22t13:26:33", DateTime{Date{2016, 03, 22}, Time{13, 26, 33, 0}}, false},
} {
gotDateTime, err := ParseDateTime(test.str)
if err != nil {
t.Errorf("ParseDateTime(%q): got error: %v", test.str, err)
continue
}
if gotDateTime != test.dateTime {
t.Errorf("ParseDateTime(%q) = %+v, want %+v", test.str, gotDateTime, test.dateTime)
}
if test.roundTrip {
gotStr := test.dateTime.String()
if gotStr != test.str {
t.Errorf("%#v.String() = %q, want %q", test.dateTime, gotStr, test.str)
}
}
}
}
func TestParseDateTimeErrors(t *testing.T) {
for _, str := range []string{
"",
"2016-03-22", // just a date
"13:26:33", // just a time
"2016-03-22 13:26:33", // wrong separating character
"2016-03-22T13:26:33x", // extra at end
} {
if _, err := ParseDateTime(str); err == nil {
t.Errorf("ParseDateTime(%q) succeeded, want error", str)
}
}
}
func TestDateTimeOf(t *testing.T) {
for _, test := range []struct {
time time.Time
want DateTime
}{
{time.Date(2014, 8, 20, 15, 8, 43, 1, time.Local),
DateTime{Date{2014, 8, 20}, Time{15, 8, 43, 1}}},
{time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC),
DateTime{Date{1, 1, 1}, Time{0, 0, 0, 0}}},
} {
if got := DateTimeOf(test.time); got != test.want {
t.Errorf("DateTimeOf(%v) = %+v, want %+v", test.time, got, test.want)
}
}
}
func TestDateTimeIsValid(t *testing.T) {
// No need to be exhaustive here; it's just Date.IsValid && Time.IsValid.
for _, test := range []struct {
dt DateTime
want bool
}{
{DateTime{Date{2016, 3, 20}, Time{0, 0, 0, 0}}, true},
{DateTime{Date{2016, -3, 20}, Time{0, 0, 0, 0}}, false},
{DateTime{Date{2016, 3, 20}, Time{24, 0, 0, 0}}, false},
} {
got := test.dt.IsValid()
if got != test.want {
t.Errorf("%#v: got %t, want %t", test.dt, got, test.want)
}
}
}
func TestDateTimeIn(t *testing.T) {
dt := DateTime{Date{2016, 1, 2}, Time{3, 4, 5, 6}}
got := dt.In(time.UTC)
want := time.Date(2016, 1, 2, 3, 4, 5, 6, time.UTC)
if !got.Equal(want) {
t.Errorf("got %v, want %v", got, want)
}
}
func TestDateTimeBefore(t *testing.T) {
d1 := Date{2016, 12, 31}
d2 := Date{2017, 1, 1}
t1 := Time{5, 6, 7, 8}
t2 := Time{5, 6, 7, 9}
for _, test := range []struct {
dt1, dt2 DateTime
want bool
}{
{DateTime{d1, t1}, DateTime{d2, t1}, true},
{DateTime{d1, t1}, DateTime{d1, t2}, true},
{DateTime{d2, t1}, DateTime{d1, t1}, false},
{DateTime{d2, t1}, DateTime{d2, t1}, false},
} {
if got := test.dt1.Before(test.dt2); got != test.want {
t.Errorf("%v.Before(%v): got %t, want %t", test.dt1, test.dt2, got, test.want)
}
}
}
func TestDateTimeAfter(t *testing.T) {
d1 := Date{2016, 12, 31}
d2 := Date{2017, 1, 1}
t1 := Time{5, 6, 7, 8}
t2 := Time{5, 6, 7, 9}
for _, test := range []struct {
dt1, dt2 DateTime
want bool
}{
{DateTime{d1, t1}, DateTime{d2, t1}, false},
{DateTime{d1, t1}, DateTime{d1, t2}, false},
{DateTime{d2, t1}, DateTime{d1, t1}, true},
{DateTime{d2, t1}, DateTime{d2, t1}, false},
} {
if got := test.dt1.After(test.dt2); got != test.want {
t.Errorf("%v.After(%v): got %t, want %t", test.dt1, test.dt2, got, test.want)
}
}
}
func TestMarshalJSON(t *testing.T) {
for _, test := range []struct {
value interface{}
want string
}{
{Date{1987, 4, 15}, `"1987-04-15"`},
{Time{18, 54, 2, 0}, `"18:54:02"`},
{DateTime{Date{1987, 4, 15}, Time{18, 54, 2, 0}}, `"1987-04-15T18:54:02"`},
} {
bgot, err := json.Marshal(test.value)
if err != nil {
t.Fatal(err)
}
if got := string(bgot); got != test.want {
t.Errorf("%#v: got %s, want %s", test.value, got, test.want)
}
}
}
func TestUnmarshalJSON(t *testing.T) {
var d Date
var tm Time
var dt DateTime
for _, test := range []struct {
data string
ptr interface{}
want interface{}
}{
{`"1987-04-15"`, &d, &Date{1987, 4, 15}},
{`"1987-04-\u0031\u0035"`, &d, &Date{1987, 4, 15}},
{`"18:54:02"`, &tm, &Time{18, 54, 2, 0}},
{`"1987-04-15T18:54:02"`, &dt, &DateTime{Date{1987, 4, 15}, Time{18, 54, 2, 0}}},
} {
if err := json.Unmarshal([]byte(test.data), test.ptr); err != nil {
t.Fatalf("%s: %v", test.data, err)
}
if !cmp.Equal(test.ptr, test.want) {
t.Errorf("%s: got %#v, want %#v", test.data, test.ptr, test.want)
}
}
for _, bad := range []string{"", `""`, `"bad"`, `"1987-04-15x"`,
`19870415`, // a JSON number
`11987-04-15x`, // not a JSON string
} {
if json.Unmarshal([]byte(bad), &d) == nil {
t.Errorf("%q, Date: got nil, want error", bad)
}
if json.Unmarshal([]byte(bad), &tm) == nil {
t.Errorf("%q, Time: got nil, want error", bad)
}
if json.Unmarshal([]byte(bad), &dt) == nil {
t.Errorf("%q, DateTime: got nil, want error", bad)
}
}
}
// Copyright 2014 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package cloud is the root of the packages used to access Google Cloud
Services. See https://godoc.org/cloud.google.com/go for a full list
of sub-packages.
Client Options
All clients in sub-packages are configurable via client options. These options are
described here: https://godoc.org/google.golang.org/api/option.
Authentication and Authorization
All the clients in sub-packages support authentication via Google Application Default
Credentials (see https://cloud.google.com/docs/authentication/production), or
by providing a JSON key file for a Service Account. See the authentication examples
in this package for details.
Timeouts and Cancellation
By default, all requests in sub-packages will run indefinitely, retrying on transient
errors when correctness allows. To set timeouts or arrange for cancellation, use
contexts. See the examples for details.
Do not attempt to control the initial connection (dialing) of a service by setting a
timeout on the context passed to NewClient. Dialing is non-blocking, so timeouts
would be ineffective and would only interfere with credential refreshing, which uses
the same context.
Connection Pooling
Connection pooling differs in clients based on their transport. Cloud
clients either rely on HTTP or gRPC transports to communicate
with Google Cloud.
Cloud clients that use HTTP (bigquery, compute, storage, and translate) rely on the
underlying HTTP transport to cache connections for later re-use. These are cached to
the default http.MaxIdleConns and http.MaxIdleConnsPerHost settings in
http.DefaultTransport.
For gRPC clients (all others in this repo), connection pooling is configurable. Users
of cloud client libraries may specify option.WithGRPCConnectionPool(n) as a client
option to NewClient calls. This configures the underlying gRPC connections to be
pooled and addressed in a round robin fashion.
Using the Libraries with Docker
Minimal docker images like Alpine lack CA certificates. This causes RPCs to appear to
hang, because gRPC retries indefinitely. See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/928
for more information.
Debugging
To see gRPC logs, set the environment variable GRPC_GO_LOG_SEVERITY_LEVEL. See
https://godoc.org/google.golang.org/grpc/grpclog for more information.
For HTTP logging, set the GODEBUG environment variable to "http2debug=1" or "http2debug=2".
*/
package cloud // import "cloud.google.com/go"
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// AUTO-GENERATED CODE. DO NOT EDIT.
package cloudtasks
import (
"context"
"fmt"
"math"
"time"
"github.com/golang/protobuf/proto"
gax "github.com/googleapis/gax-go"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
"google.golang.org/api/transport"
taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2beta2"
iampb "google.golang.org/genproto/googleapis/iam/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
)
// CallOptions contains the retry settings for each method of Client.
type CallOptions struct {
ListQueues []gax.CallOption
GetQueue []gax.CallOption
CreateQueue []gax.CallOption
UpdateQueue []gax.CallOption
DeleteQueue []gax.CallOption
PurgeQueue []gax.CallOption
PauseQueue []gax.CallOption
ResumeQueue []gax.CallOption
GetIamPolicy []gax.CallOption
SetIamPolicy []gax.CallOption
TestIamPermissions []gax.CallOption
ListTasks []gax.CallOption
GetTask []gax.CallOption
CreateTask []gax.CallOption
DeleteTask []gax.CallOption
LeaseTasks []gax.CallOption
AcknowledgeTask []gax.CallOption
RenewLease []gax.CallOption
CancelLease []gax.CallOption
RunTask []gax.CallOption
}
func defaultClientOptions() []option.ClientOption {
return []option.ClientOption{
option.WithEndpoint("cloudtasks.googleapis.com:443"),
option.WithScopes(DefaultAuthScopes()...),
}
}
func defaultCallOptions() *CallOptions {
retry := map[[2]string][]gax.CallOption{
{"default", "idempotent"}: {
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 100 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 1.3,
})
}),
},
}
return &CallOptions{
ListQueues: retry[[2]string{"default", "idempotent"}],
GetQueue: retry[[2]string{"default", "idempotent"}],
CreateQueue: retry[[2]string{"default", "non_idempotent"}],
UpdateQueue: retry[[2]string{"default", "non_idempotent"}],
DeleteQueue: retry[[2]string{"default", "idempotent"}],
PurgeQueue: retry[[2]string{"default", "non_idempotent"}],
PauseQueue: retry[[2]string{"default", "non_idempotent"}],
ResumeQueue: retry[[2]string{"default", "non_idempotent"}],
GetIamPolicy: retry[[2]string{"default", "idempotent"}],
SetIamPolicy: retry[[2]string{"default", "non_idempotent"}],
TestIamPermissions: retry[[2]string{"default", "idempotent"}],
ListTasks: retry[[2]string{"default", "idempotent"}],
GetTask: retry[[2]string{"default", "idempotent"}],
CreateTask: retry[[2]string{"default", "non_idempotent"}],
DeleteTask: retry[[2]string{"default", "idempotent"}],
LeaseTasks: retry[[2]string{"default", "non_idempotent"}],
AcknowledgeTask: retry[[2]string{"default", "non_idempotent"}],
RenewLease: retry[[2]string{"default", "non_idempotent"}],
CancelLease: retry[[2]string{"default", "non_idempotent"}],
RunTask: retry[[2]string{"default", "non_idempotent"}],
}
}
// Client is a client for interacting with Cloud Tasks API.
//
// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls.
type Client struct {
// The connection to the service.
conn *grpc.ClientConn
// The gRPC API client.
client taskspb.CloudTasksClient
// The call options for this service.
CallOptions *CallOptions
// The x-goog-* metadata to be sent with each request.
xGoogMetadata metadata.MD
}
// NewClient creates a new cloud tasks client.
//
// Cloud Tasks allows developers to manage the execution of background
// work in their applications.
func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
conn, err := transport.DialGRPC(ctx, append(defaultClientOptions(), opts...)...)
if err != nil {
return nil, err
}
c := &Client{
conn: conn,
CallOptions: defaultCallOptions(),
client: taskspb.NewCloudTasksClient(conn),
}
c.setGoogleClientInfo()
return c, nil
}
// Connection returns the client's connection to the API service.
func (c *Client) Connection() *grpc.ClientConn {
return c.conn
}
// Close closes the connection to the API service. The user should invoke this when
// the client is no longer required.
func (c *Client) Close() error {
return c.conn.Close()
}
// setGoogleClientInfo sets the name and version of the application in
// the `x-goog-api-client` header passed on each request. Intended for
// use by Google-written clients.
func (c *Client) setGoogleClientInfo(keyval ...string) {
kv := append([]string{"gl-go", versionGo()}, keyval...)
kv = append(kv, "gapic", versionClient, "gax", gax.Version, "grpc", grpc.Version)
c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
}
// ListQueues lists queues.
//
// Queues are returned in lexicographical order.
func (c *Client) ListQueues(ctx context.Context, req *taskspb.ListQueuesRequest, opts ...gax.CallOption) *QueueIterator {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.ListQueues[0:len(c.CallOptions.ListQueues):len(c.CallOptions.ListQueues)], opts...)
it := &QueueIterator{}
req = proto.Clone(req).(*taskspb.ListQueuesRequest)
it.InternalFetch = func(pageSize int, pageToken string) ([]*taskspb.Queue, string, error) {
var resp *taskspb.ListQueuesResponse
req.PageToken = pageToken
if pageSize > math.MaxInt32 {
req.PageSize = math.MaxInt32
} else {
req.PageSize = int32(pageSize)
}
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ListQueues(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, "", err
}
return resp.Queues, resp.NextPageToken, nil
}
fetch := func(pageSize int, pageToken string) (string, error) {
items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
if err != nil {
return "", err
}
it.items = append(it.items, items...)
return nextPageToken, nil
}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
it.pageInfo.MaxSize = int(req.PageSize)
return it
}
// GetQueue gets a queue.
func (c *Client) GetQueue(ctx context.Context, req *taskspb.GetQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.GetQueue[0:len(c.CallOptions.GetQueue):len(c.CallOptions.GetQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// CreateQueue creates a queue.
//
// Queues created with this method allow tasks to live for a maximum of 31
// days. After a task is 31 days old, the task will be deleted regardless of whether
// it was dispatched or not.
//
// WARNING: Using this method may have unintended side effects if you are
// using an App Engine queue.yaml or queue.xml file to manage your queues.
// Read
// Overview of Queue Management and queue.yaml (at https://cloud.google.com/tasks/docs/queue-yaml)
// before using this method.
func (c *Client) CreateQueue(ctx context.Context, req *taskspb.CreateQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.CreateQueue[0:len(c.CallOptions.CreateQueue):len(c.CallOptions.CreateQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.CreateQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// UpdateQueue updates a queue.
//
// This method creates the queue if it does not exist and updates
// the queue if it does exist.
//
// Queues created with this method allow tasks to live for a maximum of 31
// days. After a task is 31 days old, the task will be deleted regardless of whether
// it was dispatched or not.
//
// WARNING: Using this method may have unintended side effects if you are
// using an App Engine queue.yaml or queue.xml file to manage your queues.
// Read
// Overview of Queue Management and queue.yaml (at https://cloud.google.com/tasks/docs/queue-yaml)
// before using this method.
func (c *Client) UpdateQueue(ctx context.Context, req *taskspb.UpdateQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "queue.name", req.GetQueue().GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.UpdateQueue[0:len(c.CallOptions.UpdateQueue):len(c.CallOptions.UpdateQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.UpdateQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// DeleteQueue deletes a queue.
//
// This command will delete the queue even if it has tasks in it.
//
// Note: If you delete a queue, a queue with the same name can't be created
// for 7 days.
//
// WARNING: Using this method may have unintended side effects if you are
// using an App Engine queue.yaml or queue.xml file to manage your queues.
// Read
// Overview of Queue Management and queue.yaml (at https://cloud.google.com/tasks/docs/queue-yaml)
// before using this method.
func (c *Client) DeleteQueue(ctx context.Context, req *taskspb.DeleteQueueRequest, opts ...gax.CallOption) error {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.DeleteQueue[0:len(c.CallOptions.DeleteQueue):len(c.CallOptions.DeleteQueue)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
_, err = c.client.DeleteQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
return err
}
// PurgeQueue purges a queue by deleting all of its tasks.
//
// All tasks created before this method is called are permanently deleted.
//
// Purge operations can take up to one minute to take effect. Tasks
// might be dispatched before the purge takes effect. A purge is irreversible.
func (c *Client) PurgeQueue(ctx context.Context, req *taskspb.PurgeQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.PurgeQueue[0:len(c.CallOptions.PurgeQueue):len(c.CallOptions.PurgeQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.PurgeQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// PauseQueue pauses the queue.
//
// If a queue is paused then the system will stop dispatching tasks
// until the queue is resumed via
// [ResumeQueue][google.cloud.tasks.v2beta2.CloudTasks.ResumeQueue]. Tasks can still be added
// when the queue is paused. A queue is paused if its
// [state][google.cloud.tasks.v2beta2.Queue.state] is [PAUSED][google.cloud.tasks.v2beta2.Queue.State.PAUSED].
func (c *Client) PauseQueue(ctx context.Context, req *taskspb.PauseQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.PauseQueue[0:len(c.CallOptions.PauseQueue):len(c.CallOptions.PauseQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.PauseQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// ResumeQueue resume a queue.
//
// This method resumes a queue after it has been
// [PAUSED][google.cloud.tasks.v2beta2.Queue.State.PAUSED] or
// [DISABLED][google.cloud.tasks.v2beta2.Queue.State.DISABLED]. The state of a queue is stored
// in the queue's [state][google.cloud.tasks.v2beta2.Queue.state]; after calling this method it
// will be set to [RUNNING][google.cloud.tasks.v2beta2.Queue.State.RUNNING].
//
// WARNING: Resuming many high-QPS queues at the same time can
// lead to target overloading. If you are resuming high-QPS
// queues, follow the 500/50/5 pattern described in
// Managing Cloud Tasks Scaling Risks (at https://cloud.google.com/tasks/docs/manage-cloud-task-scaling).
func (c *Client) ResumeQueue(ctx context.Context, req *taskspb.ResumeQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.ResumeQueue[0:len(c.CallOptions.ResumeQueue):len(c.CallOptions.ResumeQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ResumeQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// GetIamPolicy gets the access control policy for a [Queue][google.cloud.tasks.v2beta2.Queue].
// Returns an empty policy if the resource exists and does not have a policy
// set.
//
// Authorization requires the following
// Google IAM (at https://cloud.google.com/iam) permission on the specified
// resource parent:
//
// cloudtasks.queues.getIamPolicy
func (c *Client) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", req.GetResource()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.GetIamPolicy[0:len(c.CallOptions.GetIamPolicy):len(c.CallOptions.GetIamPolicy)], opts...)
var resp *iampb.Policy
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetIamPolicy(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// SetIamPolicy sets the access control policy for a [Queue][google.cloud.tasks.v2beta2.Queue]. Replaces any existing
// policy.
//
// Note: The Cloud Console does not check queue-level IAM permissions yet.
// Project-level permissions are required to use the Cloud Console.
//
// Authorization requires the following
// Google IAM (at https://cloud.google.com/iam) permission on the specified
// resource parent:
//
// cloudtasks.queues.setIamPolicy
func (c *Client) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", req.GetResource()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.SetIamPolicy[0:len(c.CallOptions.SetIamPolicy):len(c.CallOptions.SetIamPolicy)], opts...)
var resp *iampb.Policy
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.SetIamPolicy(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// TestIamPermissions returns permissions that a caller has on a [Queue][google.cloud.tasks.v2beta2.Queue].
// If the resource does not exist, this will return an empty set of
// permissions, not a [NOT_FOUND][google.rpc.Code.NOT_FOUND] error.
//
// Note: This operation is designed to be used for building permission-aware
// UIs and command-line tools, not for authorization checking. This operation
// may "fail open" without warning.
func (c *Client) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", req.GetResource()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.TestIamPermissions[0:len(c.CallOptions.TestIamPermissions):len(c.CallOptions.TestIamPermissions)], opts...)
var resp *iampb.TestIamPermissionsResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.TestIamPermissions(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// ListTasks lists the tasks in a queue.
//
// By default, only the [BASIC][google.cloud.tasks.v2beta2.Task.View.BASIC] view is retrieved
// due to performance considerations;
// [response_view][google.cloud.tasks.v2beta2.ListTasksRequest.response_view] controls the
// subset of information which is returned.
//
// The tasks may be returned in any order. The ordering may change at any
// time.
func (c *Client) ListTasks(ctx context.Context, req *taskspb.ListTasksRequest, opts ...gax.CallOption) *TaskIterator {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.ListTasks[0:len(c.CallOptions.ListTasks):len(c.CallOptions.ListTasks)], opts...)
it := &TaskIterator{}
req = proto.Clone(req).(*taskspb.ListTasksRequest)
it.InternalFetch = func(pageSize int, pageToken string) ([]*taskspb.Task, string, error) {
var resp *taskspb.ListTasksResponse
req.PageToken = pageToken
if pageSize > math.MaxInt32 {
req.PageSize = math.MaxInt32
} else {
req.PageSize = int32(pageSize)
}
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ListTasks(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, "", err
}
return resp.Tasks, resp.NextPageToken, nil
}
fetch := func(pageSize int, pageToken string) (string, error) {
items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
if err != nil {
return "", err
}
it.items = append(it.items, items...)
return nextPageToken, nil
}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
it.pageInfo.MaxSize = int(req.PageSize)
return it
}
// GetTask gets a task.
func (c *Client) GetTask(ctx context.Context, req *taskspb.GetTaskRequest, opts ...gax.CallOption) (*taskspb.Task, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.GetTask[0:len(c.CallOptions.GetTask):len(c.CallOptions.GetTask)], opts...)
var resp *taskspb.Task
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetTask(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// CreateTask creates a task and adds it to a queue.
//
// Tasks cannot be updated after creation; there is no UpdateTask command.
//
// For [App Engine queues][google.cloud.tasks.v2beta2.AppEngineHttpTarget], the maximum task size is
// 100KB.
//
// For [pull queues][google.cloud.tasks.v2beta2.PullTarget], the maximum task size is 1MB.
func (c *Client) CreateTask(ctx context.Context, req *taskspb.CreateTaskRequest, opts ...gax.CallOption) (*taskspb.Task, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.CreateTask[0:len(c.CallOptions.CreateTask):len(c.CallOptions.CreateTask)], opts...)
var resp *taskspb.Task
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.CreateTask(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// DeleteTask deletes a task.
//
// A task can be deleted if it is scheduled or dispatched. A task
// cannot be deleted if it has completed successfully or permanently
// failed.
func (c *Client) DeleteTask(ctx context.Context, req *taskspb.DeleteTaskRequest, opts ...gax.CallOption) error {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.DeleteTask[0:len(c.CallOptions.DeleteTask):len(c.CallOptions.DeleteTask)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
_, err = c.client.DeleteTask(ctx, req, settings.GRPC...)
return err
}, opts...)
return err
}
// LeaseTasks leases tasks from a pull queue for
// [lease_duration][google.cloud.tasks.v2beta2.LeaseTasksRequest.lease_duration].
//
// This method is invoked by the worker to obtain a lease. The
// worker must acknowledge the task via
// [AcknowledgeTask][google.cloud.tasks.v2beta2.CloudTasks.AcknowledgeTask] after they have
// performed the work associated with the task.
//
// The [payload][google.cloud.tasks.v2beta2.PullMessage.payload] is intended to store data that
// the worker needs to perform the work associated with the task. To
// return the payloads in the [response][google.cloud.tasks.v2beta2.LeaseTasksResponse], set
// [response_view][google.cloud.tasks.v2beta2.LeaseTasksRequest.response_view] to
// [FULL][google.cloud.tasks.v2beta2.Task.View.FULL].
//
// A maximum of 10 qps of [LeaseTasks][google.cloud.tasks.v2beta2.CloudTasks.LeaseTasks]
// requests are allowed per
// queue. [RESOURCE_EXHAUSTED][google.rpc.Code.RESOURCE_EXHAUSTED]
// is returned when this limit is
// exceeded. [RESOURCE_EXHAUSTED][google.rpc.Code.RESOURCE_EXHAUSTED]
// is also returned when
// [max_tasks_dispatched_per_second][google.cloud.tasks.v2beta2.RateLimits.max_tasks_dispatched_per_second]
// is exceeded.
func (c *Client) LeaseTasks(ctx context.Context, req *taskspb.LeaseTasksRequest, opts ...gax.CallOption) (*taskspb.LeaseTasksResponse, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.LeaseTasks[0:len(c.CallOptions.LeaseTasks):len(c.CallOptions.LeaseTasks)], opts...)
var resp *taskspb.LeaseTasksResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.LeaseTasks(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// AcknowledgeTask acknowledges a pull task.
//
// The worker, that is, the entity that
// [leased][google.cloud.tasks.v2beta2.CloudTasks.LeaseTasks] this task must call this method
// to indicate that the work associated with the task has finished.
//
// The worker must acknowledge a task within the
// [lease_duration][google.cloud.tasks.v2beta2.LeaseTasksRequest.lease_duration] or the lease
// will expire and the task will become available to be leased
// again. After the task is acknowledged, it will not be returned
// by a later [LeaseTasks][google.cloud.tasks.v2beta2.CloudTasks.LeaseTasks],
// [GetTask][google.cloud.tasks.v2beta2.CloudTasks.GetTask], or
// [ListTasks][google.cloud.tasks.v2beta2.CloudTasks.ListTasks].
func (c *Client) AcknowledgeTask(ctx context.Context, req *taskspb.AcknowledgeTaskRequest, opts ...gax.CallOption) error {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.AcknowledgeTask[0:len(c.CallOptions.AcknowledgeTask):len(c.CallOptions.AcknowledgeTask)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
_, err = c.client.AcknowledgeTask(ctx, req, settings.GRPC...)
return err
}, opts...)
return err
}
// RenewLease renew the current lease of a pull task.
//
// The worker can use this method to extend the lease by a new
// duration, starting from now. The new task lease will be
// returned in the task's [schedule_time][google.cloud.tasks.v2beta2.Task.schedule_time].
func (c *Client) RenewLease(ctx context.Context, req *taskspb.RenewLeaseRequest, opts ...gax.CallOption) (*taskspb.Task, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.RenewLease[0:len(c.CallOptions.RenewLease):len(c.CallOptions.RenewLease)], opts...)
var resp *taskspb.Task
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.RenewLease(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// CancelLease cancel a pull task's lease.
//
// The worker can use this method to cancel a task's lease by
// setting its [schedule_time][google.cloud.tasks.v2beta2.Task.schedule_time] to now. This will
// make the task available to be leased to the next caller of
// [LeaseTasks][google.cloud.tasks.v2beta2.CloudTasks.LeaseTasks].
func (c *Client) CancelLease(ctx context.Context, req *taskspb.CancelLeaseRequest, opts ...gax.CallOption) (*taskspb.Task, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.CancelLease[0:len(c.CallOptions.CancelLease):len(c.CallOptions.CancelLease)], opts...)
var resp *taskspb.Task
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.CancelLease(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// RunTask forces a task to run now.
//
// When this method is called, Cloud Tasks will dispatch the task, even if
// the task is already running, the queue has reached its [RateLimits][google.cloud.tasks.v2beta2.RateLimits] or
// is [PAUSED][google.cloud.tasks.v2beta2.Queue.State.PAUSED].
//
// This command is meant to be used for manual debugging. For
// example, [RunTask][google.cloud.tasks.v2beta2.CloudTasks.RunTask] can be used to retry a failed
// task after a fix has been made or to manually force a task to be
// dispatched now.
//
// The dispatched task is returned. That is, the task that is returned
// contains the [status][google.cloud.tasks.v2beta2.Task.status] after the task is dispatched but
// before the task is received by its target.
//
// If Cloud Tasks receives a successful response from the task's
// target, then the task will be deleted; otherwise the task's
// [schedule_time][google.cloud.tasks.v2beta2.Task.schedule_time] will be reset to the time that
// [RunTask][google.cloud.tasks.v2beta2.CloudTasks.RunTask] was called plus the retry delay specified
// in the queue's [RetryConfig][google.cloud.tasks.v2beta2.RetryConfig].
//
// [RunTask][google.cloud.tasks.v2beta2.CloudTasks.RunTask] returns
// [NOT_FOUND][google.rpc.Code.NOT_FOUND] when it is called on a
// task that has already succeeded or permanently failed.
//
// [RunTask][google.cloud.tasks.v2beta2.CloudTasks.RunTask] cannot be called on a
// [pull task][google.cloud.tasks.v2beta2.PullMessage].
func (c *Client) RunTask(ctx context.Context, req *taskspb.RunTaskRequest, opts ...gax.CallOption) (*taskspb.Task, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.RunTask[0:len(c.CallOptions.RunTask):len(c.CallOptions.RunTask)], opts...)
var resp *taskspb.Task
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.RunTask(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// QueueIterator manages a stream of *taskspb.Queue.
type QueueIterator struct {
items []*taskspb.Queue
pageInfo *iterator.PageInfo
nextFunc func() error
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*taskspb.Queue, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *QueueIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *QueueIterator) Next() (*taskspb.Queue, error) {
var item *taskspb.Queue
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *QueueIterator) bufLen() int {
return len(it.items)
}
func (it *QueueIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// TaskIterator manages a stream of *taskspb.Task.
type TaskIterator struct {
items []*taskspb.Task
pageInfo *iterator.PageInfo
nextFunc func() error
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*taskspb.Task, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *TaskIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *TaskIterator) Next() (*taskspb.Task, error) {
var item *taskspb.Task
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *TaskIterator) bufLen() int {
return len(it.items)
}
func (it *TaskIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// AUTO-GENERATED CODE. DO NOT EDIT.
package cloudtasks_test
import (
"context"
cloudtasks "cloud.google.com/go/cloudtasks/apiv2beta2"
"google.golang.org/api/iterator"
taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2beta2"
iampb "google.golang.org/genproto/googleapis/iam/v1"
)
func ExampleNewClient() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
// TODO: Use client.
_ = c
}
func ExampleClient_ListQueues() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.ListQueuesRequest{
// TODO: Fill request struct fields.
}
it := c.ListQueues(ctx, req)
for {
resp, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
}
func ExampleClient_GetQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.GetQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.GetQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_CreateQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.CreateQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.CreateQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_UpdateQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.UpdateQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.UpdateQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_DeleteQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.DeleteQueueRequest{
// TODO: Fill request struct fields.
}
err = c.DeleteQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
}
func ExampleClient_PurgeQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.PurgeQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.PurgeQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_PauseQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.PauseQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.PauseQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_ResumeQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.ResumeQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.ResumeQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_GetIamPolicy() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &iampb.GetIamPolicyRequest{
// TODO: Fill request struct fields.
}
resp, err := c.GetIamPolicy(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_SetIamPolicy() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &iampb.SetIamPolicyRequest{
// TODO: Fill request struct fields.
}
resp, err := c.SetIamPolicy(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_TestIamPermissions() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &iampb.TestIamPermissionsRequest{
// TODO: Fill request struct fields.
}
resp, err := c.TestIamPermissions(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_ListTasks() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.ListTasksRequest{
// TODO: Fill request struct fields.
}
it := c.ListTasks(ctx, req)
for {
resp, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
}
func ExampleClient_GetTask() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.GetTaskRequest{
// TODO: Fill request struct fields.
}
resp, err := c.GetTask(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_CreateTask() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.CreateTaskRequest{
// TODO: Fill request struct fields.
}
resp, err := c.CreateTask(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_DeleteTask() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.DeleteTaskRequest{
// TODO: Fill request struct fields.
}
err = c.DeleteTask(ctx, req)
if err != nil {
// TODO: Handle error.
}
}
func ExampleClient_LeaseTasks() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.LeaseTasksRequest{
// TODO: Fill request struct fields.
}
resp, err := c.LeaseTasks(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_AcknowledgeTask() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.AcknowledgeTaskRequest{
// TODO: Fill request struct fields.
}
err = c.AcknowledgeTask(ctx, req)
if err != nil {
// TODO: Handle error.
}
}
func ExampleClient_RenewLease() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.RenewLeaseRequest{
// TODO: Fill request struct fields.
}
resp, err := c.RenewLease(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_CancelLease() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.CancelLeaseRequest{
// TODO: Fill request struct fields.
}
resp, err := c.CancelLease(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_RunTask() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.RunTaskRequest{
// TODO: Fill request struct fields.
}
resp, err := c.RunTask(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// AUTO-GENERATED CODE. DO NOT EDIT.
// Package cloudtasks is an auto-generated package for the
// Cloud Tasks API.
//
// NOTE: This package is in alpha. It is not stable, and is likely to change.
//
// Manages the execution of large numbers of distributed requests.
package cloudtasks // import "cloud.google.com/go/cloudtasks/apiv2beta2"
import (
"context"
"runtime"
"strings"
"unicode"
"google.golang.org/grpc/metadata"
)
func insertMetadata(ctx context.Context, mds ...metadata.MD) context.Context {
out, _ := metadata.FromOutgoingContext(ctx)
out = out.Copy()
for _, md := range mds {
for k, v := range md {
out[k] = append(out[k], v...)
}
}
return metadata.NewOutgoingContext(ctx, out)
}
// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
func DefaultAuthScopes() []string {
return []string{
"https://www.googleapis.com/auth/cloud-platform",
}
}
// versionGo returns the Go runtime version. The returned string
// has no whitespace, suitable for reporting in header.
func versionGo() string {
const develPrefix = "devel +"
s := runtime.Version()
if strings.HasPrefix(s, develPrefix) {
s = s[len(develPrefix):]
if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
s = s[:p]
}
return s
}
notSemverRune := func(r rune) bool {
return strings.IndexRune("0123456789.", r) < 0
}
if strings.HasPrefix(s, "go1") {
s = s[2:]
var prerelease string
if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
s, prerelease = s[:p], s[p:]
}
if strings.HasSuffix(s, ".") {
s += "0"
} else if strings.Count(s, ".") < 2 {
s += ".0"
}
if prerelease != "" {
s += "-" + prerelease
}
return s
}
return "UNKNOWN"
}
const versionClient = "20181129"
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// AUTO-GENERATED CODE. DO NOT EDIT.
package cloudtasks
import (
durationpb "github.com/golang/protobuf/ptypes/duration"
emptypb "github.com/golang/protobuf/ptypes/empty"
timestamppb "github.com/golang/protobuf/ptypes/timestamp"
taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2beta2"
iampb "google.golang.org/genproto/googleapis/iam/v1"
)
import (
"context"
"flag"
"fmt"
"io"
"log"
"net"
"os"
"strings"
"testing"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"google.golang.org/api/option"
status "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
gstatus "google.golang.org/grpc/status"
)
var _ = io.EOF
var _ = ptypes.MarshalAny
var _ status.Status
type mockCloudTasksServer struct {
// Embed for forward compatibility.
// Tests will keep working if more methods are added
// in the future.
taskspb.CloudTasksServer
reqs []proto.Message
// If set, all calls return this error.
err error
// responses to return if err == nil
resps []proto.Message
}
func (s *mockCloudTasksServer) ListQueues(ctx context.Context, req *taskspb.ListQueuesRequest) (*taskspb.ListQueuesResponse, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.ListQueuesResponse), nil
}
func (s *mockCloudTasksServer) GetQueue(ctx context.Context, req *taskspb.GetQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) CreateQueue(ctx context.Context, req *taskspb.CreateQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) UpdateQueue(ctx context.Context, req *taskspb.UpdateQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) DeleteQueue(ctx context.Context, req *taskspb.DeleteQueueRequest) (*emptypb.Empty, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*emptypb.Empty), nil
}
func (s *mockCloudTasksServer) PurgeQueue(ctx context.Context, req *taskspb.PurgeQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) PauseQueue(ctx context.Context, req *taskspb.PauseQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) ResumeQueue(ctx context.Context, req *taskspb.ResumeQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest) (*iampb.Policy, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*iampb.Policy), nil
}
func (s *mockCloudTasksServer) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest) (*iampb.Policy, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*iampb.Policy), nil
}
func (s *mockCloudTasksServer) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest) (*iampb.TestIamPermissionsResponse, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*iampb.TestIamPermissionsResponse), nil
}
func (s *mockCloudTasksServer) ListTasks(ctx context.Context, req *taskspb.ListTasksRequest) (*taskspb.ListTasksResponse, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.ListTasksResponse), nil
}
func (s *mockCloudTasksServer) GetTask(ctx context.Context, req *taskspb.GetTaskRequest) (*taskspb.Task, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Task), nil
}
func (s *mockCloudTasksServer) CreateTask(ctx context.Context, req *taskspb.CreateTaskRequest) (*taskspb.Task, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Task), nil
}
func (s *mockCloudTasksServer) DeleteTask(ctx context.Context, req *taskspb.DeleteTaskRequest) (*emptypb.Empty, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*emptypb.Empty), nil
}
func (s *mockCloudTasksServer) LeaseTasks(ctx context.Context, req *taskspb.LeaseTasksRequest) (*taskspb.LeaseTasksResponse, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.LeaseTasksResponse), nil
}
func (s *mockCloudTasksServer) AcknowledgeTask(ctx context.Context, req *taskspb.AcknowledgeTaskRequest) (*emptypb.Empty, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*emptypb.Empty), nil
}
func (s *mockCloudTasksServer) RenewLease(ctx context.Context, req *taskspb.RenewLeaseRequest) (*taskspb.Task, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Task), nil
}
func (s *mockCloudTasksServer) CancelLease(ctx context.Context, req *taskspb.CancelLeaseRequest) (*taskspb.Task, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Task), nil
}
func (s *mockCloudTasksServer) RunTask(ctx context.Context, req *taskspb.RunTaskRequest) (*taskspb.Task, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Task), nil
}
// clientOpt is the option tests should use to connect to the test server.
// It is initialized by TestMain.
var clientOpt option.ClientOption
var (
mockCloudTasks mockCloudTasksServer
)
func TestMain(m *testing.M) {
flag.Parse()
serv := grpc.NewServer()
taskspb.RegisterCloudTasksServer(serv, &mockCloudTasks)
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
log.Fatal(err)
}
go serv.Serve(lis)
conn, err := grpc.Dial(lis.Addr().String(), grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
clientOpt = option.WithGRPCConn(conn)
os.Exit(m.Run())
}
func TestCloudTasksListQueues(t *testing.T) {
var nextPageToken string = ""
var queuesElement *taskspb.Queue = &taskspb.Queue{}
var queues = []*taskspb.Queue{queuesElement}
var expectedResponse = &taskspb.ListQueuesResponse{
NextPageToken: nextPageToken,
Queues: queues,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]")
var request = &taskspb.ListQueuesRequest{
Parent: formattedParent,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ListQueues(context.Background(), request).Next()
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
want := (interface{})(expectedResponse.Queues[0])
got := (interface{})(resp)
var ok bool
switch want := (want).(type) {
case proto.Message:
ok = proto.Equal(want, got.(proto.Message))
default:
ok = want == got
}
if !ok {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksListQueuesError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]")
var request = &taskspb.ListQueuesRequest{
Parent: formattedParent,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ListQueues(context.Background(), request).Next()
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksGetQueue(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Queue{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.GetQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksGetQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.GetQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksCreateQueue(t *testing.T) {
var name string = "name3373707"
var expectedResponse = &taskspb.Queue{
Name: name,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]")
var queue *taskspb.Queue = &taskspb.Queue{}
var request = &taskspb.CreateQueueRequest{
Parent: formattedParent,
Queue: queue,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.CreateQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksCreateQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]")
var queue *taskspb.Queue = &taskspb.Queue{}
var request = &taskspb.CreateQueueRequest{
Parent: formattedParent,
Queue: queue,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.CreateQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksUpdateQueue(t *testing.T) {
var name string = "name3373707"
var expectedResponse = &taskspb.Queue{
Name: name,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var queue *taskspb.Queue = &taskspb.Queue{}
var request = &taskspb.UpdateQueueRequest{
Queue: queue,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.UpdateQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksUpdateQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var queue *taskspb.Queue = &taskspb.Queue{}
var request = &taskspb.UpdateQueueRequest{
Queue: queue,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.UpdateQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksDeleteQueue(t *testing.T) {
var expectedResponse *emptypb.Empty = &emptypb.Empty{}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.DeleteQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
err = c.DeleteQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
}
func TestCloudTasksDeleteQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.DeleteQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
err = c.DeleteQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
}
func TestCloudTasksPurgeQueue(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Queue{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.PurgeQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.PurgeQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksPurgeQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.PurgeQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.PurgeQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksPauseQueue(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Queue{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.PauseQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.PauseQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksPauseQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.PauseQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.PauseQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksResumeQueue(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Queue{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.ResumeQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ResumeQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksResumeQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.ResumeQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ResumeQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksGetIamPolicy(t *testing.T) {
var version int32 = 351608024
var etag []byte = []byte("21")
var expectedResponse = &iampb.Policy{
Version: version,
Etag: etag,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &iampb.GetIamPolicyRequest{
Resource: formattedResource,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetIamPolicy(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksGetIamPolicyError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &iampb.GetIamPolicyRequest{
Resource: formattedResource,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetIamPolicy(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksSetIamPolicy(t *testing.T) {
var version int32 = 351608024
var etag []byte = []byte("21")
var expectedResponse = &iampb.Policy{
Version: version,
Etag: etag,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var policy *iampb.Policy = &iampb.Policy{}
var request = &iampb.SetIamPolicyRequest{
Resource: formattedResource,
Policy: policy,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.SetIamPolicy(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksSetIamPolicyError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var policy *iampb.Policy = &iampb.Policy{}
var request = &iampb.SetIamPolicyRequest{
Resource: formattedResource,
Policy: policy,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.SetIamPolicy(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksTestIamPermissions(t *testing.T) {
var expectedResponse *iampb.TestIamPermissionsResponse = &iampb.TestIamPermissionsResponse{}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var permissions []string = nil
var request = &iampb.TestIamPermissionsRequest{
Resource: formattedResource,
Permissions: permissions,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.TestIamPermissions(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksTestIamPermissionsError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var permissions []string = nil
var request = &iampb.TestIamPermissionsRequest{
Resource: formattedResource,
Permissions: permissions,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.TestIamPermissions(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksListTasks(t *testing.T) {
var nextPageToken string = ""
var tasksElement *taskspb.Task = &taskspb.Task{}
var tasks = []*taskspb.Task{tasksElement}
var expectedResponse = &taskspb.ListTasksResponse{
NextPageToken: nextPageToken,
Tasks: tasks,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.ListTasksRequest{
Parent: formattedParent,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ListTasks(context.Background(), request).Next()
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
want := (interface{})(expectedResponse.Tasks[0])
got := (interface{})(resp)
var ok bool
switch want := (want).(type) {
case proto.Message:
ok = proto.Equal(want, got.(proto.Message))
default:
ok = want == got
}
if !ok {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksListTasksError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.ListTasksRequest{
Parent: formattedParent,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ListTasks(context.Background(), request).Next()
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksGetTask(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Task{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.GetTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetTask(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksGetTaskError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.GetTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetTask(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksCreateTask(t *testing.T) {
var name string = "name3373707"
var expectedResponse = &taskspb.Task{
Name: name,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var task *taskspb.Task = &taskspb.Task{}
var request = &taskspb.CreateTaskRequest{
Parent: formattedParent,
Task: task,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.CreateTask(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksCreateTaskError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var task *taskspb.Task = &taskspb.Task{}
var request = &taskspb.CreateTaskRequest{
Parent: formattedParent,
Task: task,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.CreateTask(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksDeleteTask(t *testing.T) {
var expectedResponse *emptypb.Empty = &emptypb.Empty{}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.DeleteTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
err = c.DeleteTask(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
}
func TestCloudTasksDeleteTaskError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.DeleteTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
err = c.DeleteTask(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
}
func TestCloudTasksLeaseTasks(t *testing.T) {
var expectedResponse *taskspb.LeaseTasksResponse = &taskspb.LeaseTasksResponse{}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var leaseDuration *durationpb.Duration = &durationpb.Duration{}
var request = &taskspb.LeaseTasksRequest{
Parent: formattedParent,
LeaseDuration: leaseDuration,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.LeaseTasks(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksLeaseTasksError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var leaseDuration *durationpb.Duration = &durationpb.Duration{}
var request = &taskspb.LeaseTasksRequest{
Parent: formattedParent,
LeaseDuration: leaseDuration,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.LeaseTasks(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksAcknowledgeTask(t *testing.T) {
var expectedResponse *emptypb.Empty = &emptypb.Empty{}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var scheduleTime *timestamppb.Timestamp = &timestamppb.Timestamp{}
var request = &taskspb.AcknowledgeTaskRequest{
Name: formattedName,
ScheduleTime: scheduleTime,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
err = c.AcknowledgeTask(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
}
func TestCloudTasksAcknowledgeTaskError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var scheduleTime *timestamppb.Timestamp = &timestamppb.Timestamp{}
var request = &taskspb.AcknowledgeTaskRequest{
Name: formattedName,
ScheduleTime: scheduleTime,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
err = c.AcknowledgeTask(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
}
func TestCloudTasksRenewLease(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Task{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var scheduleTime *timestamppb.Timestamp = &timestamppb.Timestamp{}
var leaseDuration *durationpb.Duration = &durationpb.Duration{}
var request = &taskspb.RenewLeaseRequest{
Name: formattedName,
ScheduleTime: scheduleTime,
LeaseDuration: leaseDuration,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.RenewLease(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksRenewLeaseError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var scheduleTime *timestamppb.Timestamp = &timestamppb.Timestamp{}
var leaseDuration *durationpb.Duration = &durationpb.Duration{}
var request = &taskspb.RenewLeaseRequest{
Name: formattedName,
ScheduleTime: scheduleTime,
LeaseDuration: leaseDuration,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.RenewLease(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksCancelLease(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Task{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var scheduleTime *timestamppb.Timestamp = &timestamppb.Timestamp{}
var request = &taskspb.CancelLeaseRequest{
Name: formattedName,
ScheduleTime: scheduleTime,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.CancelLease(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksCancelLeaseError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var scheduleTime *timestamppb.Timestamp = &timestamppb.Timestamp{}
var request = &taskspb.CancelLeaseRequest{
Name: formattedName,
ScheduleTime: scheduleTime,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.CancelLease(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksRunTask(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Task{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.RunTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.RunTask(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksRunTaskError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.RunTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.RunTask(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// AUTO-GENERATED CODE. DO NOT EDIT.
package cloudtasks
import (
"context"
"fmt"
"math"
"time"
"github.com/golang/protobuf/proto"
gax "github.com/googleapis/gax-go"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
"google.golang.org/api/transport"
taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2beta3"
iampb "google.golang.org/genproto/googleapis/iam/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
)
// CallOptions contains the retry settings for each method of Client.
type CallOptions struct {
ListQueues []gax.CallOption
GetQueue []gax.CallOption
CreateQueue []gax.CallOption
UpdateQueue []gax.CallOption
DeleteQueue []gax.CallOption
PurgeQueue []gax.CallOption
PauseQueue []gax.CallOption
ResumeQueue []gax.CallOption
GetIamPolicy []gax.CallOption
SetIamPolicy []gax.CallOption
TestIamPermissions []gax.CallOption
ListTasks []gax.CallOption
GetTask []gax.CallOption
CreateTask []gax.CallOption
DeleteTask []gax.CallOption
RunTask []gax.CallOption
}
func defaultClientOptions() []option.ClientOption {
return []option.ClientOption{
option.WithEndpoint("cloudtasks.googleapis.com:443"),
option.WithScopes(DefaultAuthScopes()...),
}
}
func defaultCallOptions() *CallOptions {
retry := map[[2]string][]gax.CallOption{
{"default", "idempotent"}: {
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 100 * time.Millisecond,
Max: 60000 * time.Millisecond,
Multiplier: 1.3,
})
}),
},
}
return &CallOptions{
ListQueues: retry[[2]string{"default", "idempotent"}],
GetQueue: retry[[2]string{"default", "idempotent"}],
CreateQueue: retry[[2]string{"default", "non_idempotent"}],
UpdateQueue: retry[[2]string{"default", "non_idempotent"}],
DeleteQueue: retry[[2]string{"default", "idempotent"}],
PurgeQueue: retry[[2]string{"default", "non_idempotent"}],
PauseQueue: retry[[2]string{"default", "non_idempotent"}],
ResumeQueue: retry[[2]string{"default", "non_idempotent"}],
GetIamPolicy: retry[[2]string{"default", "idempotent"}],
SetIamPolicy: retry[[2]string{"default", "non_idempotent"}],
TestIamPermissions: retry[[2]string{"default", "idempotent"}],
ListTasks: retry[[2]string{"default", "idempotent"}],
GetTask: retry[[2]string{"default", "idempotent"}],
CreateTask: retry[[2]string{"default", "non_idempotent"}],
DeleteTask: retry[[2]string{"default", "idempotent"}],
RunTask: retry[[2]string{"default", "non_idempotent"}],
}
}
// Client is a client for interacting with Cloud Tasks API.
//
// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls.
type Client struct {
// The connection to the service.
conn *grpc.ClientConn
// The gRPC API client.
client taskspb.CloudTasksClient
// The call options for this service.
CallOptions *CallOptions
// The x-goog-* metadata to be sent with each request.
xGoogMetadata metadata.MD
}
// NewClient creates a new cloud tasks client.
//
// Cloud Tasks allows developers to manage the execution of background
// work in their applications.
func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
conn, err := transport.DialGRPC(ctx, append(defaultClientOptions(), opts...)...)
if err != nil {
return nil, err
}
c := &Client{
conn: conn,
CallOptions: defaultCallOptions(),
client: taskspb.NewCloudTasksClient(conn),
}
c.setGoogleClientInfo()
return c, nil
}
// Connection returns the client's connection to the API service.
func (c *Client) Connection() *grpc.ClientConn {
return c.conn
}
// Close closes the connection to the API service. The user should invoke this when
// the client is no longer required.
func (c *Client) Close() error {
return c.conn.Close()
}
// setGoogleClientInfo sets the name and version of the application in
// the `x-goog-api-client` header passed on each request. Intended for
// use by Google-written clients.
func (c *Client) setGoogleClientInfo(keyval ...string) {
kv := append([]string{"gl-go", versionGo()}, keyval...)
kv = append(kv, "gapic", versionClient, "gax", gax.Version, "grpc", grpc.Version)
c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
}
// ListQueues lists queues.
//
// Queues are returned in lexicographical order.
func (c *Client) ListQueues(ctx context.Context, req *taskspb.ListQueuesRequest, opts ...gax.CallOption) *QueueIterator {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.ListQueues[0:len(c.CallOptions.ListQueues):len(c.CallOptions.ListQueues)], opts...)
it := &QueueIterator{}
req = proto.Clone(req).(*taskspb.ListQueuesRequest)
it.InternalFetch = func(pageSize int, pageToken string) ([]*taskspb.Queue, string, error) {
var resp *taskspb.ListQueuesResponse
req.PageToken = pageToken
if pageSize > math.MaxInt32 {
req.PageSize = math.MaxInt32
} else {
req.PageSize = int32(pageSize)
}
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ListQueues(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, "", err
}
return resp.Queues, resp.NextPageToken, nil
}
fetch := func(pageSize int, pageToken string) (string, error) {
items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
if err != nil {
return "", err
}
it.items = append(it.items, items...)
return nextPageToken, nil
}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
it.pageInfo.MaxSize = int(req.PageSize)
return it
}
// GetQueue gets a queue.
func (c *Client) GetQueue(ctx context.Context, req *taskspb.GetQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.GetQueue[0:len(c.CallOptions.GetQueue):len(c.CallOptions.GetQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// CreateQueue creates a queue.
//
// Queues created with this method allow tasks to live for a maximum of 31
// days. After a task is 31 days old, the task will be deleted regardless of whether
// it was dispatched or not.
//
// WARNING: Using this method may have unintended side effects if you are
// using an App Engine queue.yaml or queue.xml file to manage your queues.
// Read
// Overview of Queue Management and queue.yaml (at https://cloud.google.com/tasks/docs/queue-yaml)
// before using this method.
func (c *Client) CreateQueue(ctx context.Context, req *taskspb.CreateQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.CreateQueue[0:len(c.CallOptions.CreateQueue):len(c.CallOptions.CreateQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.CreateQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// UpdateQueue updates a queue.
//
// This method creates the queue if it does not exist and updates
// the queue if it does exist.
//
// Queues created with this method allow tasks to live for a maximum of 31
// days. After a task is 31 days old, the task will be deleted regardless of whether
// it was dispatched or not.
//
// WARNING: Using this method may have unintended side effects if you are
// using an App Engine queue.yaml or queue.xml file to manage your queues.
// Read
// Overview of Queue Management and queue.yaml (at https://cloud.google.com/tasks/docs/queue-yaml)
// before using this method.
func (c *Client) UpdateQueue(ctx context.Context, req *taskspb.UpdateQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "queue.name", req.GetQueue().GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.UpdateQueue[0:len(c.CallOptions.UpdateQueue):len(c.CallOptions.UpdateQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.UpdateQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// DeleteQueue deletes a queue.
//
// This command will delete the queue even if it has tasks in it.
//
// Note: If you delete a queue, a queue with the same name can't be created
// for 7 days.
//
// WARNING: Using this method may have unintended side effects if you are
// using an App Engine queue.yaml or queue.xml file to manage your queues.
// Read
// Overview of Queue Management and queue.yaml (at https://cloud.google.com/tasks/docs/queue-yaml)
// before using this method.
func (c *Client) DeleteQueue(ctx context.Context, req *taskspb.DeleteQueueRequest, opts ...gax.CallOption) error {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.DeleteQueue[0:len(c.CallOptions.DeleteQueue):len(c.CallOptions.DeleteQueue)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
_, err = c.client.DeleteQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
return err
}
// PurgeQueue purges a queue by deleting all of its tasks.
//
// All tasks created before this method is called are permanently deleted.
//
// Purge operations can take up to one minute to take effect. Tasks
// might be dispatched before the purge takes effect. A purge is irreversible.
func (c *Client) PurgeQueue(ctx context.Context, req *taskspb.PurgeQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.PurgeQueue[0:len(c.CallOptions.PurgeQueue):len(c.CallOptions.PurgeQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.PurgeQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// PauseQueue pauses the queue.
//
// If a queue is paused then the system will stop dispatching tasks
// until the queue is resumed via
// [ResumeQueue][google.cloud.tasks.v2beta3.CloudTasks.ResumeQueue]. Tasks can still be added
// when the queue is paused. A queue is paused if its
// [state][google.cloud.tasks.v2beta3.Queue.state] is [PAUSED][google.cloud.tasks.v2beta3.Queue.State.PAUSED].
func (c *Client) PauseQueue(ctx context.Context, req *taskspb.PauseQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.PauseQueue[0:len(c.CallOptions.PauseQueue):len(c.CallOptions.PauseQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.PauseQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// ResumeQueue resume a queue.
//
// This method resumes a queue after it has been
// [PAUSED][google.cloud.tasks.v2beta3.Queue.State.PAUSED] or
// [DISABLED][google.cloud.tasks.v2beta3.Queue.State.DISABLED]. The state of a queue is stored
// in the queue's [state][google.cloud.tasks.v2beta3.Queue.state]; after calling this method it
// will be set to [RUNNING][google.cloud.tasks.v2beta3.Queue.State.RUNNING].
//
// WARNING: Resuming many high-QPS queues at the same time can
// lead to target overloading. If you are resuming high-QPS
// queues, follow the 500/50/5 pattern described in
// Managing Cloud Tasks Scaling Risks (at https://cloud.google.com/tasks/docs/manage-cloud-task-scaling).
func (c *Client) ResumeQueue(ctx context.Context, req *taskspb.ResumeQueueRequest, opts ...gax.CallOption) (*taskspb.Queue, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.ResumeQueue[0:len(c.CallOptions.ResumeQueue):len(c.CallOptions.ResumeQueue)], opts...)
var resp *taskspb.Queue
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ResumeQueue(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// GetIamPolicy gets the access control policy for a [Queue][google.cloud.tasks.v2beta3.Queue].
// Returns an empty policy if the resource exists and does not have a policy
// set.
//
// Authorization requires the following
// Google IAM (at https://cloud.google.com/iam) permission on the specified
// resource parent:
//
// cloudtasks.queues.getIamPolicy
func (c *Client) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", req.GetResource()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.GetIamPolicy[0:len(c.CallOptions.GetIamPolicy):len(c.CallOptions.GetIamPolicy)], opts...)
var resp *iampb.Policy
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetIamPolicy(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// SetIamPolicy sets the access control policy for a [Queue][google.cloud.tasks.v2beta3.Queue]. Replaces any existing
// policy.
//
// Note: The Cloud Console does not check queue-level IAM permissions yet.
// Project-level permissions are required to use the Cloud Console.
//
// Authorization requires the following
// Google IAM (at https://cloud.google.com/iam) permission on the specified
// resource parent:
//
// cloudtasks.queues.setIamPolicy
func (c *Client) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", req.GetResource()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.SetIamPolicy[0:len(c.CallOptions.SetIamPolicy):len(c.CallOptions.SetIamPolicy)], opts...)
var resp *iampb.Policy
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.SetIamPolicy(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// TestIamPermissions returns permissions that a caller has on a [Queue][google.cloud.tasks.v2beta3.Queue].
// If the resource does not exist, this will return an empty set of
// permissions, not a [NOT_FOUND][google.rpc.Code.NOT_FOUND] error.
//
// Note: This operation is designed to be used for building permission-aware
// UIs and command-line tools, not for authorization checking. This operation
// may "fail open" without warning.
func (c *Client) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", req.GetResource()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.TestIamPermissions[0:len(c.CallOptions.TestIamPermissions):len(c.CallOptions.TestIamPermissions)], opts...)
var resp *iampb.TestIamPermissionsResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.TestIamPermissions(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// ListTasks lists the tasks in a queue.
//
// By default, only the [BASIC][google.cloud.tasks.v2beta3.Task.View.BASIC] view is retrieved
// due to performance considerations;
// [response_view][google.cloud.tasks.v2beta3.ListTasksRequest.response_view] controls the
// subset of information which is returned.
//
// The tasks may be returned in any order. The ordering may change at any
// time.
func (c *Client) ListTasks(ctx context.Context, req *taskspb.ListTasksRequest, opts ...gax.CallOption) *TaskIterator {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.ListTasks[0:len(c.CallOptions.ListTasks):len(c.CallOptions.ListTasks)], opts...)
it := &TaskIterator{}
req = proto.Clone(req).(*taskspb.ListTasksRequest)
it.InternalFetch = func(pageSize int, pageToken string) ([]*taskspb.Task, string, error) {
var resp *taskspb.ListTasksResponse
req.PageToken = pageToken
if pageSize > math.MaxInt32 {
req.PageSize = math.MaxInt32
} else {
req.PageSize = int32(pageSize)
}
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ListTasks(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, "", err
}
return resp.Tasks, resp.NextPageToken, nil
}
fetch := func(pageSize int, pageToken string) (string, error) {
items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
if err != nil {
return "", err
}
it.items = append(it.items, items...)
return nextPageToken, nil
}
it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
it.pageInfo.MaxSize = int(req.PageSize)
return it
}
// GetTask gets a task.
func (c *Client) GetTask(ctx context.Context, req *taskspb.GetTaskRequest, opts ...gax.CallOption) (*taskspb.Task, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.GetTask[0:len(c.CallOptions.GetTask):len(c.CallOptions.GetTask)], opts...)
var resp *taskspb.Task
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetTask(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// CreateTask creates a task and adds it to a queue.
//
// Tasks cannot be updated after creation; there is no UpdateTask command.
//
// For [App Engine queues][google.cloud.tasks.v2beta3.AppEngineHttpQueue], the maximum task size is
// 100KB.
func (c *Client) CreateTask(ctx context.Context, req *taskspb.CreateTaskRequest, opts ...gax.CallOption) (*taskspb.Task, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", req.GetParent()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.CreateTask[0:len(c.CallOptions.CreateTask):len(c.CallOptions.CreateTask)], opts...)
var resp *taskspb.Task
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.CreateTask(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// DeleteTask deletes a task.
//
// A task can be deleted if it is scheduled or dispatched. A task
// cannot be deleted if it has executed successfully or permanently
// failed.
func (c *Client) DeleteTask(ctx context.Context, req *taskspb.DeleteTaskRequest, opts ...gax.CallOption) error {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.DeleteTask[0:len(c.CallOptions.DeleteTask):len(c.CallOptions.DeleteTask)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
_, err = c.client.DeleteTask(ctx, req, settings.GRPC...)
return err
}, opts...)
return err
}
// RunTask forces a task to run now.
//
// When this method is called, Cloud Tasks will dispatch the task, even if
// the task is already running, the queue has reached its [RateLimits][google.cloud.tasks.v2beta3.RateLimits] or
// is [PAUSED][google.cloud.tasks.v2beta3.Queue.State.PAUSED].
//
// This command is meant to be used for manual debugging. For
// example, [RunTask][google.cloud.tasks.v2beta3.CloudTasks.RunTask] can be used to retry a failed
// task after a fix has been made or to manually force a task to be
// dispatched now.
//
// The dispatched task is returned. That is, the task that is returned
// contains the [status][Task.status] after the task is dispatched but
// before the task is received by its target.
//
// If Cloud Tasks receives a successful response from the task's
// target, then the task will be deleted; otherwise the task's
// [schedule_time][google.cloud.tasks.v2beta3.Task.schedule_time] will be reset to the time that
// [RunTask][google.cloud.tasks.v2beta3.CloudTasks.RunTask] was called plus the retry delay specified
// in the queue's [RetryConfig][google.cloud.tasks.v2beta3.RetryConfig].
//
// [RunTask][google.cloud.tasks.v2beta3.CloudTasks.RunTask] returns
// [NOT_FOUND][google.rpc.Code.NOT_FOUND] when it is called on a
// task that has already succeeded or permanently failed.
func (c *Client) RunTask(ctx context.Context, req *taskspb.RunTaskRequest, opts ...gax.CallOption) (*taskspb.Task, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append(c.CallOptions.RunTask[0:len(c.CallOptions.RunTask):len(c.CallOptions.RunTask)], opts...)
var resp *taskspb.Task
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.RunTask(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return resp, nil
}
// QueueIterator manages a stream of *taskspb.Queue.
type QueueIterator struct {
items []*taskspb.Queue
pageInfo *iterator.PageInfo
nextFunc func() error
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*taskspb.Queue, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *QueueIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *QueueIterator) Next() (*taskspb.Queue, error) {
var item *taskspb.Queue
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *QueueIterator) bufLen() int {
return len(it.items)
}
func (it *QueueIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// TaskIterator manages a stream of *taskspb.Task.
type TaskIterator struct {
items []*taskspb.Task
pageInfo *iterator.PageInfo
nextFunc func() error
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*taskspb.Task, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *TaskIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *TaskIterator) Next() (*taskspb.Task, error) {
var item *taskspb.Task
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *TaskIterator) bufLen() int {
return len(it.items)
}
func (it *TaskIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// AUTO-GENERATED CODE. DO NOT EDIT.
package cloudtasks_test
import (
"context"
cloudtasks "cloud.google.com/go/cloudtasks/apiv2beta3"
"google.golang.org/api/iterator"
taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2beta3"
iampb "google.golang.org/genproto/googleapis/iam/v1"
)
func ExampleNewClient() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
// TODO: Use client.
_ = c
}
func ExampleClient_ListQueues() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.ListQueuesRequest{
// TODO: Fill request struct fields.
}
it := c.ListQueues(ctx, req)
for {
resp, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
}
func ExampleClient_GetQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.GetQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.GetQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_CreateQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.CreateQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.CreateQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_UpdateQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.UpdateQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.UpdateQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_DeleteQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.DeleteQueueRequest{
// TODO: Fill request struct fields.
}
err = c.DeleteQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
}
func ExampleClient_PurgeQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.PurgeQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.PurgeQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_PauseQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.PauseQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.PauseQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_ResumeQueue() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.ResumeQueueRequest{
// TODO: Fill request struct fields.
}
resp, err := c.ResumeQueue(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_GetIamPolicy() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &iampb.GetIamPolicyRequest{
// TODO: Fill request struct fields.
}
resp, err := c.GetIamPolicy(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_SetIamPolicy() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &iampb.SetIamPolicyRequest{
// TODO: Fill request struct fields.
}
resp, err := c.SetIamPolicy(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_TestIamPermissions() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &iampb.TestIamPermissionsRequest{
// TODO: Fill request struct fields.
}
resp, err := c.TestIamPermissions(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_ListTasks() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.ListTasksRequest{
// TODO: Fill request struct fields.
}
it := c.ListTasks(ctx, req)
for {
resp, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
}
func ExampleClient_GetTask() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.GetTaskRequest{
// TODO: Fill request struct fields.
}
resp, err := c.GetTask(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_CreateTask() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.CreateTaskRequest{
// TODO: Fill request struct fields.
}
resp, err := c.CreateTask(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
func ExampleClient_DeleteTask() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.DeleteTaskRequest{
// TODO: Fill request struct fields.
}
err = c.DeleteTask(ctx, req)
if err != nil {
// TODO: Handle error.
}
}
func ExampleClient_RunTask() {
ctx := context.Background()
c, err := cloudtasks.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
req := &taskspb.RunTaskRequest{
// TODO: Fill request struct fields.
}
resp, err := c.RunTask(ctx, req)
if err != nil {
// TODO: Handle error.
}
// TODO: Use resp.
_ = resp
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// AUTO-GENERATED CODE. DO NOT EDIT.
// Package cloudtasks is an auto-generated package for the
// Cloud Tasks API.
//
// NOTE: This package is in alpha. It is not stable, and is likely to change.
//
// Manages the execution of large numbers of distributed requests.
package cloudtasks // import "cloud.google.com/go/cloudtasks/apiv2beta3"
import (
"context"
"runtime"
"strings"
"unicode"
"google.golang.org/grpc/metadata"
)
func insertMetadata(ctx context.Context, mds ...metadata.MD) context.Context {
out, _ := metadata.FromOutgoingContext(ctx)
out = out.Copy()
for _, md := range mds {
for k, v := range md {
out[k] = append(out[k], v...)
}
}
return metadata.NewOutgoingContext(ctx, out)
}
// DefaultAuthScopes reports the default set of authentication scopes to use with this package.
func DefaultAuthScopes() []string {
return []string{
"https://www.googleapis.com/auth/cloud-platform",
}
}
// versionGo returns the Go runtime version. The returned string
// has no whitespace, suitable for reporting in header.
func versionGo() string {
const develPrefix = "devel +"
s := runtime.Version()
if strings.HasPrefix(s, develPrefix) {
s = s[len(develPrefix):]
if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
s = s[:p]
}
return s
}
notSemverRune := func(r rune) bool {
return strings.IndexRune("0123456789.", r) < 0
}
if strings.HasPrefix(s, "go1") {
s = s[2:]
var prerelease string
if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
s, prerelease = s[:p], s[p:]
}
if strings.HasSuffix(s, ".") {
s += "0"
} else if strings.Count(s, ".") < 2 {
s += ".0"
}
if prerelease != "" {
s += "-" + prerelease
}
return s
}
return "UNKNOWN"
}
const versionClient = "20181129"
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// AUTO-GENERATED CODE. DO NOT EDIT.
package cloudtasks
import (
emptypb "github.com/golang/protobuf/ptypes/empty"
taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2beta3"
iampb "google.golang.org/genproto/googleapis/iam/v1"
)
import (
"context"
"flag"
"fmt"
"io"
"log"
"net"
"os"
"strings"
"testing"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"google.golang.org/api/option"
status "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
gstatus "google.golang.org/grpc/status"
)
var _ = io.EOF
var _ = ptypes.MarshalAny
var _ status.Status
type mockCloudTasksServer struct {
// Embed for forward compatibility.
// Tests will keep working if more methods are added
// in the future.
taskspb.CloudTasksServer
reqs []proto.Message
// If set, all calls return this error.
err error
// responses to return if err == nil
resps []proto.Message
}
func (s *mockCloudTasksServer) ListQueues(ctx context.Context, req *taskspb.ListQueuesRequest) (*taskspb.ListQueuesResponse, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.ListQueuesResponse), nil
}
func (s *mockCloudTasksServer) GetQueue(ctx context.Context, req *taskspb.GetQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) CreateQueue(ctx context.Context, req *taskspb.CreateQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) UpdateQueue(ctx context.Context, req *taskspb.UpdateQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) DeleteQueue(ctx context.Context, req *taskspb.DeleteQueueRequest) (*emptypb.Empty, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*emptypb.Empty), nil
}
func (s *mockCloudTasksServer) PurgeQueue(ctx context.Context, req *taskspb.PurgeQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) PauseQueue(ctx context.Context, req *taskspb.PauseQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) ResumeQueue(ctx context.Context, req *taskspb.ResumeQueueRequest) (*taskspb.Queue, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Queue), nil
}
func (s *mockCloudTasksServer) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest) (*iampb.Policy, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*iampb.Policy), nil
}
func (s *mockCloudTasksServer) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest) (*iampb.Policy, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*iampb.Policy), nil
}
func (s *mockCloudTasksServer) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest) (*iampb.TestIamPermissionsResponse, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*iampb.TestIamPermissionsResponse), nil
}
func (s *mockCloudTasksServer) ListTasks(ctx context.Context, req *taskspb.ListTasksRequest) (*taskspb.ListTasksResponse, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.ListTasksResponse), nil
}
func (s *mockCloudTasksServer) GetTask(ctx context.Context, req *taskspb.GetTaskRequest) (*taskspb.Task, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Task), nil
}
func (s *mockCloudTasksServer) CreateTask(ctx context.Context, req *taskspb.CreateTaskRequest) (*taskspb.Task, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Task), nil
}
func (s *mockCloudTasksServer) DeleteTask(ctx context.Context, req *taskspb.DeleteTaskRequest) (*emptypb.Empty, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*emptypb.Empty), nil
}
func (s *mockCloudTasksServer) RunTask(ctx context.Context, req *taskspb.RunTaskRequest) (*taskspb.Task, error) {
md, _ := metadata.FromIncomingContext(ctx)
if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
}
s.reqs = append(s.reqs, req)
if s.err != nil {
return nil, s.err
}
return s.resps[0].(*taskspb.Task), nil
}
// clientOpt is the option tests should use to connect to the test server.
// It is initialized by TestMain.
var clientOpt option.ClientOption
var (
mockCloudTasks mockCloudTasksServer
)
func TestMain(m *testing.M) {
flag.Parse()
serv := grpc.NewServer()
taskspb.RegisterCloudTasksServer(serv, &mockCloudTasks)
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
log.Fatal(err)
}
go serv.Serve(lis)
conn, err := grpc.Dial(lis.Addr().String(), grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
clientOpt = option.WithGRPCConn(conn)
os.Exit(m.Run())
}
func TestCloudTasksListQueues(t *testing.T) {
var nextPageToken string = ""
var queuesElement *taskspb.Queue = &taskspb.Queue{}
var queues = []*taskspb.Queue{queuesElement}
var expectedResponse = &taskspb.ListQueuesResponse{
NextPageToken: nextPageToken,
Queues: queues,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]")
var request = &taskspb.ListQueuesRequest{
Parent: formattedParent,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ListQueues(context.Background(), request).Next()
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
want := (interface{})(expectedResponse.Queues[0])
got := (interface{})(resp)
var ok bool
switch want := (want).(type) {
case proto.Message:
ok = proto.Equal(want, got.(proto.Message))
default:
ok = want == got
}
if !ok {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksListQueuesError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]")
var request = &taskspb.ListQueuesRequest{
Parent: formattedParent,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ListQueues(context.Background(), request).Next()
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksGetQueue(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Queue{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.GetQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksGetQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.GetQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksCreateQueue(t *testing.T) {
var name string = "name3373707"
var expectedResponse = &taskspb.Queue{
Name: name,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]")
var queue *taskspb.Queue = &taskspb.Queue{}
var request = &taskspb.CreateQueueRequest{
Parent: formattedParent,
Queue: queue,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.CreateQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksCreateQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s", "[PROJECT]", "[LOCATION]")
var queue *taskspb.Queue = &taskspb.Queue{}
var request = &taskspb.CreateQueueRequest{
Parent: formattedParent,
Queue: queue,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.CreateQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksUpdateQueue(t *testing.T) {
var name string = "name3373707"
var expectedResponse = &taskspb.Queue{
Name: name,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var queue *taskspb.Queue = &taskspb.Queue{}
var request = &taskspb.UpdateQueueRequest{
Queue: queue,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.UpdateQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksUpdateQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var queue *taskspb.Queue = &taskspb.Queue{}
var request = &taskspb.UpdateQueueRequest{
Queue: queue,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.UpdateQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksDeleteQueue(t *testing.T) {
var expectedResponse *emptypb.Empty = &emptypb.Empty{}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.DeleteQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
err = c.DeleteQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
}
func TestCloudTasksDeleteQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.DeleteQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
err = c.DeleteQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
}
func TestCloudTasksPurgeQueue(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Queue{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.PurgeQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.PurgeQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksPurgeQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.PurgeQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.PurgeQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksPauseQueue(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Queue{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.PauseQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.PauseQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksPauseQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.PauseQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.PauseQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksResumeQueue(t *testing.T) {
var name2 string = "name2-1052831874"
var expectedResponse = &taskspb.Queue{
Name: name2,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.ResumeQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ResumeQueue(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksResumeQueueError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.ResumeQueueRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ResumeQueue(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksGetIamPolicy(t *testing.T) {
var version int32 = 351608024
var etag []byte = []byte("21")
var expectedResponse = &iampb.Policy{
Version: version,
Etag: etag,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &iampb.GetIamPolicyRequest{
Resource: formattedResource,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetIamPolicy(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksGetIamPolicyError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &iampb.GetIamPolicyRequest{
Resource: formattedResource,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetIamPolicy(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksSetIamPolicy(t *testing.T) {
var version int32 = 351608024
var etag []byte = []byte("21")
var expectedResponse = &iampb.Policy{
Version: version,
Etag: etag,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var policy *iampb.Policy = &iampb.Policy{}
var request = &iampb.SetIamPolicyRequest{
Resource: formattedResource,
Policy: policy,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.SetIamPolicy(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksSetIamPolicyError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var policy *iampb.Policy = &iampb.Policy{}
var request = &iampb.SetIamPolicyRequest{
Resource: formattedResource,
Policy: policy,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.SetIamPolicy(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksTestIamPermissions(t *testing.T) {
var expectedResponse *iampb.TestIamPermissionsResponse = &iampb.TestIamPermissionsResponse{}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var permissions []string = nil
var request = &iampb.TestIamPermissionsRequest{
Resource: formattedResource,
Permissions: permissions,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.TestIamPermissions(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksTestIamPermissionsError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedResource string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var permissions []string = nil
var request = &iampb.TestIamPermissionsRequest{
Resource: formattedResource,
Permissions: permissions,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.TestIamPermissions(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksListTasks(t *testing.T) {
var nextPageToken string = ""
var tasksElement *taskspb.Task = &taskspb.Task{}
var tasks = []*taskspb.Task{tasksElement}
var expectedResponse = &taskspb.ListTasksResponse{
NextPageToken: nextPageToken,
Tasks: tasks,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.ListTasksRequest{
Parent: formattedParent,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ListTasks(context.Background(), request).Next()
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
want := (interface{})(expectedResponse.Tasks[0])
got := (interface{})(resp)
var ok bool
switch want := (want).(type) {
case proto.Message:
ok = proto.Equal(want, got.(proto.Message))
default:
ok = want == got
}
if !ok {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksListTasksError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var request = &taskspb.ListTasksRequest{
Parent: formattedParent,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.ListTasks(context.Background(), request).Next()
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksGetTask(t *testing.T) {
var name2 string = "name2-1052831874"
var dispatchCount int32 = 1217252086
var responseCount int32 = 424727441
var expectedResponse = &taskspb.Task{
Name: name2,
DispatchCount: dispatchCount,
ResponseCount: responseCount,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.GetTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetTask(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksGetTaskError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.GetTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.GetTask(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksCreateTask(t *testing.T) {
var name string = "name3373707"
var dispatchCount int32 = 1217252086
var responseCount int32 = 424727441
var expectedResponse = &taskspb.Task{
Name: name,
DispatchCount: dispatchCount,
ResponseCount: responseCount,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var task *taskspb.Task = &taskspb.Task{}
var request = &taskspb.CreateTaskRequest{
Parent: formattedParent,
Task: task,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.CreateTask(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksCreateTaskError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedParent string = fmt.Sprintf("projects/%s/locations/%s/queues/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]")
var task *taskspb.Task = &taskspb.Task{}
var request = &taskspb.CreateTaskRequest{
Parent: formattedParent,
Task: task,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.CreateTask(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
func TestCloudTasksDeleteTask(t *testing.T) {
var expectedResponse *emptypb.Empty = &emptypb.Empty{}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.DeleteTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
err = c.DeleteTask(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
}
func TestCloudTasksDeleteTaskError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.DeleteTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
err = c.DeleteTask(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
}
func TestCloudTasksRunTask(t *testing.T) {
var name2 string = "name2-1052831874"
var dispatchCount int32 = 1217252086
var responseCount int32 = 424727441
var expectedResponse = &taskspb.Task{
Name: name2,
DispatchCount: dispatchCount,
ResponseCount: responseCount,
}
mockCloudTasks.err = nil
mockCloudTasks.reqs = nil
mockCloudTasks.resps = append(mockCloudTasks.resps[:0], expectedResponse)
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.RunTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.RunTask(context.Background(), request)
if err != nil {
t.Fatal(err)
}
if want, got := request, mockCloudTasks.reqs[0]; !proto.Equal(want, got) {
t.Errorf("wrong request %q, want %q", got, want)
}
if want, got := expectedResponse, resp; !proto.Equal(want, got) {
t.Errorf("wrong response %q, want %q)", got, want)
}
}
func TestCloudTasksRunTaskError(t *testing.T) {
errCode := codes.PermissionDenied
mockCloudTasks.err = gstatus.Error(errCode, "test error")
var formattedName string = fmt.Sprintf("projects/%s/locations/%s/queues/%s/tasks/%s", "[PROJECT]", "[LOCATION]", "[QUEUE]", "[TASK]")
var request = &taskspb.RunTaskRequest{
Name: formattedName,
}
c, err := NewClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
resp, err := c.RunTask(context.Background(), request)
if st, ok := gstatus.FromError(err); !ok {
t.Errorf("got error %v, expected grpc error", err)
} else if c := st.Code(); c != errCode {
t.Errorf("got error code %q, want %q", c, errCode)
}
_ = resp
}
// Copyright 2016 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build linux,go1.7
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"math/rand"
"os"
"sync"
"time"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints"
debuglet "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/local"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector"
"cloud.google.com/go/compute/metadata"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
cd "google.golang.org/api/clouddebugger/v2"
)
var (
appModule = flag.String("appmodule", "", "Optional application module name.")
appVersion = flag.String("appversion", "", "Optional application module version name.")
sourceContextFile = flag.String("sourcecontext", "", "File containing JSON-encoded source context.")
verbose = flag.Bool("v", false, "Output verbose log messages.")
projectNumber = flag.String("projectnumber", "", "Project number."+
" If this is not set, it is read from the GCP metadata server.")
projectID = flag.String("projectid", "", "Project ID."+
" If this is not set, it is read from the GCP metadata server.")
serviceAccountFile = flag.String("serviceaccountfile", "", "File containing JSON service account credentials.")
)
const (
maxCapturedStackFrames = 50
maxCapturedVariables = 1000
)
func main() {
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) == 0 {
// The user needs to supply the name of the executable to run.
flag.Usage()
return
}
if *projectNumber == "" {
var err error
*projectNumber, err = metadata.NumericProjectID()
if err != nil {
log.Print("Debuglet initialization: ", err)
}
}
if *projectID == "" {
var err error
*projectID, err = metadata.ProjectID()
if err != nil {
log.Print("Debuglet initialization: ", err)
}
}
sourceContexts, err := readSourceContextFile(*sourceContextFile)
if err != nil {
log.Print("Reading source context file: ", err)
}
var ts oauth2.TokenSource
ctx := context.Background()
if *serviceAccountFile != "" {
if ts, err = serviceAcctTokenSource(ctx, *serviceAccountFile, cd.CloudDebuggerScope); err != nil {
log.Fatalf("Error getting credentials from file %s: %v", *serviceAccountFile, err)
}
} else if ts, err = google.DefaultTokenSource(ctx, cd.CloudDebuggerScope); err != nil {
log.Print("Error getting application default credentials for Cloud Debugger:", err)
os.Exit(103)
}
c, err := debuglet.NewController(ctx, debuglet.Options{
ProjectNumber: *projectNumber,
ProjectID: *projectID,
AppModule: *appModule,
AppVersion: *appVersion,
SourceContexts: sourceContexts,
Verbose: *verbose,
TokenSource: ts,
})
if err != nil {
log.Fatal("Error connecting to Cloud Debugger: ", err)
}
prog, err := local.New(args[0])
if err != nil {
log.Fatal("Error loading program: ", err)
}
// Load the program, but don't actually start it running yet.
if _, err = prog.Run(args[1:]...); err != nil {
log.Fatal("Error loading program: ", err)
}
bs := breakpoints.NewBreakpointStore(prog)
// Seed the random number generator.
rand.Seed(time.Now().UnixNano())
// Now we want to do two things: run the user's program, and start sending
// List requests periodically to the Debuglet Controller to get breakpoints
// to set.
//
// We want to give the Debuglet Controller a chance to give us breakpoints
// before we start the program, otherwise we would miss any breakpoint
// triggers that occur during program startup -- for example, a breakpoint on
// the first line of main. But if the Debuglet Controller is not responding or
// is returning errors, we don't want to delay starting the program
// indefinitely.
//
// We pass a channel to breakpointListLoop, which will close it when the first
// List call finishes. Then we wait until either the channel is closed or a
// 5-second timer has finished before starting the program.
ch := make(chan bool)
// Start a goroutine that sends List requests to the Debuglet Controller, and
// sets any breakpoints it gets back.
go breakpointListLoop(ctx, c, bs, ch)
// Wait until 5 seconds have passed or breakpointListLoop has closed ch.
select {
case <-time.After(5 * time.Second):
case <-ch:
}
// Run the debuggee.
programLoop(ctx, c, bs, prog)
}
// usage prints a usage message to stderr and exits.
func usage() {
me := "a.out"
if len(os.Args) >= 1 {
me = os.Args[0]
}
fmt.Fprintf(os.Stderr, "Usage of %s:\n", me)
fmt.Fprintf(os.Stderr, "\t%s [flags...] -- <program name> args...\n", me)
fmt.Fprintf(os.Stderr, "Flags:\n")
flag.PrintDefaults()
fmt.Fprintf(os.Stderr,
"See https://cloud.google.com/tools/cloud-debugger/setting-up-on-compute-engine for more information.\n")
os.Exit(2)
}
// readSourceContextFile reads a JSON-encoded source context from the given file.
// It returns a non-empty slice on success.
func readSourceContextFile(filename string) ([]*cd.SourceContext, error) {
if filename == "" {
return nil, nil
}
scJSON, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("reading file %q: %v", filename, err)
}
var sc cd.SourceContext
if err = json.Unmarshal(scJSON, &sc); err != nil {
return nil, fmt.Errorf("parsing file %q: %v", filename, err)
}
return []*cd.SourceContext{&sc}, nil
}
// breakpointListLoop repeatedly calls the Debuglet Controller's List RPC, and
// passes the results to the BreakpointStore so it can set and unset breakpoints
// in the program.
//
// After the first List call finishes, ch is closed.
func breakpointListLoop(ctx context.Context, c *debuglet.Controller, bs *breakpoints.BreakpointStore, first chan bool) {
const (
avgTimeBetweenCalls = time.Second
errorDelay = 5 * time.Second
)
// randomDuration returns a random duration with expected value avg.
randomDuration := func(avg time.Duration) time.Duration {
return time.Duration(rand.Int63n(int64(2*avg + 1)))
}
var consecutiveFailures uint
for {
callStart := time.Now()
resp, err := c.List(ctx)
if err != nil && err != debuglet.ErrListUnchanged {
log.Printf("Debuglet controller server error: %v", err)
}
if err == nil {
bs.ProcessBreakpointList(resp.Breakpoints)
}
if first != nil {
// We've finished one call to List and set any breakpoints we received.
close(first)
first = nil
}
// Asynchronously send updates for any breakpoints that caused an error when
// the BreakpointStore tried to process them. We don't wait for the update
// to finish before the program can exit, as we do for normal updates.
errorBps := bs.ErrorBreakpoints()
for _, bp := range errorBps {
go func(bp *cd.Breakpoint) {
if err := c.Update(ctx, bp.Id, bp); err != nil {
log.Printf("Failed to send breakpoint update for %s: %s", bp.Id, err)
}
}(bp)
}
// Make the next call not too soon after the one we just did.
delay := randomDuration(avgTimeBetweenCalls)
// If the call returned an error other than ErrListUnchanged, wait longer.
if err != nil && err != debuglet.ErrListUnchanged {
// Wait twice as long after each consecutive failure, to a maximum of 16x.
delay += randomDuration(errorDelay * (1 << consecutiveFailures))
if consecutiveFailures < 4 {
consecutiveFailures++
}
} else {
consecutiveFailures = 0
}
// Sleep until we reach time callStart+delay. If we've already passed that
// time, time.Sleep will return immediately -- this should be the common
// case, since the server will delay responding to List for a while when
// there are no changes to report.
time.Sleep(callStart.Add(delay).Sub(time.Now()))
}
}
// programLoop runs the program being debugged to completion. When a breakpoint's
// conditions are satisfied, it sends an Update RPC to the Debuglet Controller.
// The function returns when the program exits and all Update RPCs have finished.
func programLoop(ctx context.Context, c *debuglet.Controller, bs *breakpoints.BreakpointStore, prog debug.Program) {
var wg sync.WaitGroup
for {
// Run the program until it hits a breakpoint or exits.
status, err := prog.Resume()
if err != nil {
break
}
// Get the breakpoints at this address whose conditions were satisfied,
// and remove the ones that aren't logpoints.
bps := bs.BreakpointsAtPC(status.PC)
bps = bpsWithConditionSatisfied(bps, prog)
for _, bp := range bps {
if bp.Action != "LOG" {
bs.RemoveBreakpoint(bp)
}
}
if len(bps) == 0 {
continue
}
// Evaluate expressions and get the stack.
vc := valuecollector.NewCollector(prog, maxCapturedVariables)
needStackFrames := false
for _, bp := range bps {
// If evaluating bp's condition didn't return an error, evaluate bp's
// expressions, and later get the stack frames.
if bp.Status == nil {
bp.EvaluatedExpressions = expressionValues(bp.Expressions, prog, vc)
needStackFrames = true
}
}
var (
stack []*cd.StackFrame
stackFramesStatusMessage *cd.StatusMessage
)
if needStackFrames {
stack, stackFramesStatusMessage = stackFrames(prog, vc)
}
// Read variable values from the program.
variableTable := vc.ReadValues()
// Start a goroutine to send updates to the Debuglet Controller or write
// to logs, concurrently with resuming the program.
// TODO: retry Update on failure.
for _, bp := range bps {
wg.Add(1)
switch bp.Action {
case "LOG":
go func(format string, evaluatedExpressions []*cd.Variable) {
s := valuecollector.LogString(format, evaluatedExpressions, variableTable)
log.Print(s)
wg.Done()
}(bp.LogMessageFormat, bp.EvaluatedExpressions)
bp.Status = nil
bp.EvaluatedExpressions = nil
default:
go func(bp *cd.Breakpoint) {
defer wg.Done()
bp.IsFinalState = true
if bp.Status == nil {
// If evaluating bp's condition didn't return an error, include the
// stack frames, variable table, and any status message produced when
// getting the stack frames.
bp.StackFrames = stack
bp.VariableTable = variableTable
bp.Status = stackFramesStatusMessage
}
if err := c.Update(ctx, bp.Id, bp); err != nil {
log.Printf("Failed to send breakpoint update for %s: %s", bp.Id, err)
}
}(bp)
}
}
}
// Wait for all updates to finish before returning.
wg.Wait()
}
// bpsWithConditionSatisfied returns the breakpoints whose conditions are true
// (or that do not have a condition.)
func bpsWithConditionSatisfied(bpsIn []*cd.Breakpoint, prog debug.Program) []*cd.Breakpoint {
var bpsOut []*cd.Breakpoint
for _, bp := range bpsIn {
cond, err := condTruth(bp.Condition, prog)
if err != nil {
bp.Status = errorStatusMessage(err.Error(), refersToBreakpointCondition)
// Include bp in the list to be updated when there's an error, so that
// the user gets a response.
bpsOut = append(bpsOut, bp)
} else if cond {
bpsOut = append(bpsOut, bp)
}
}
return bpsOut
}
// condTruth evaluates a condition.
func condTruth(condition string, prog debug.Program) (bool, error) {
if condition == "" {
// A condition wasn't set.
return true, nil
}
val, err := prog.Evaluate(condition)
if err != nil {
return false, err
}
if v, ok := val.(bool); !ok {
return false, fmt.Errorf("condition expression has type %T, should be bool", val)
} else {
return v, nil
}
}
// expressionValues evaluates a slice of expressions and returns a []*cd.Variable
// containing the results.
// If the result of an expression evaluation refers to values from the program's
// memory (e.g., the expression evaluates to a slice) a corresponding variable is
// added to the value collector, to be read later.
func expressionValues(expressions []string, prog debug.Program, vc *valuecollector.Collector) []*cd.Variable {
evaluatedExpressions := make([]*cd.Variable, len(expressions))
for i, exp := range expressions {
ee := &cd.Variable{Name: exp}
evaluatedExpressions[i] = ee
if val, err := prog.Evaluate(exp); err != nil {
ee.Status = errorStatusMessage(err.Error(), refersToBreakpointExpression)
} else {
vc.FillValue(val, ee)
}
}
return evaluatedExpressions
}
// stackFrames returns a stack trace for the program. It passes references to
// function parameters and local variables to the value collector, so it can read
// their values later.
func stackFrames(prog debug.Program, vc *valuecollector.Collector) ([]*cd.StackFrame, *cd.StatusMessage) {
frames, err := prog.Frames(maxCapturedStackFrames)
if err != nil {
return nil, errorStatusMessage("Error getting stack: "+err.Error(), refersToUnspecified)
}
stackFrames := make([]*cd.StackFrame, len(frames))
for i, f := range frames {
frame := &cd.StackFrame{}
frame.Function = f.Function
for _, v := range f.Params {
frame.Arguments = append(frame.Arguments, vc.AddVariable(debug.LocalVar(v)))
}
for _, v := range f.Vars {
frame.Locals = append(frame.Locals, vc.AddVariable(v))
}
frame.Location = &cd.SourceLocation{
Path: f.File,
Line: int64(f.Line),
}
stackFrames[i] = frame
}
return stackFrames, nil
}
// errorStatusMessage returns a *cd.StatusMessage indicating an error,
// with the given message and refersTo field.
func errorStatusMessage(msg string, refersTo int) *cd.StatusMessage {
return &cd.StatusMessage{
Description: &cd.FormatMessage{Format: "$0", Parameters: []string{msg}},
IsError: true,
RefersTo: refersToString[refersTo],
}
}
const (
// RefersTo values for cd.StatusMessage.
refersToUnspecified = iota
refersToBreakpointCondition
refersToBreakpointExpression
)
// refersToString contains the strings for each refersTo value.
// See the definition of StatusMessage in the v2/clouddebugger package.
var refersToString = map[int]string{
refersToUnspecified: "UNSPECIFIED",
refersToBreakpointCondition: "BREAKPOINT_CONDITION",
refersToBreakpointExpression: "BREAKPOINT_EXPRESSION",
}
func serviceAcctTokenSource(ctx context.Context, filename string, scope ...string) (oauth2.TokenSource, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("cannot read service account file: %v", err)
}
cfg, err := google.JWTConfigFromJSON(data, scope...)
if err != nil {
return nil, fmt.Errorf("google.JWTConfigFromJSON: %v", err)
}
return cfg.TokenSource(ctx), nil
}
// Copyright 2016 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package breakpoints handles breakpoint requests we get from the user through
// the Debuglet Controller, and manages corresponding breakpoints set in the code.
package breakpoints
import (
"log"
"sync"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug"
cd "google.golang.org/api/clouddebugger/v2"
)
// BreakpointStore stores the set of breakpoints for a program.
type BreakpointStore struct {
mu sync.Mutex
// prog is the program being debugged.
prog debug.Program
// idToBreakpoint is a map from breakpoint identifier to *cd.Breakpoint. The
// map value is nil if the breakpoint is inactive. A breakpoint is active if:
// - We received it from the Debuglet Controller, and it was active at the time;
// - We were able to set code breakpoints for it;
// - We have not reached any of those code breakpoints while satisfying the
// breakpoint's conditions, or the breakpoint has action LOG; and
// - The Debuglet Controller hasn't informed us the breakpoint has become inactive.
idToBreakpoint map[string]*cd.Breakpoint
// pcToBps and bpToPCs store the many-to-many relationship between breakpoints we
// received from the Debuglet Controller and the code breakpoints we set for them.
pcToBps map[uint64][]*cd.Breakpoint
bpToPCs map[*cd.Breakpoint][]uint64
// errors contains any breakpoints which couldn't be set because they caused an
// error. These are retrieved with ErrorBreakpoints, and the caller is
// expected to handle sending updates for them.
errors []*cd.Breakpoint
}
// NewBreakpointStore returns a BreakpointStore for the given program.
func NewBreakpointStore(prog debug.Program) *BreakpointStore {
return &BreakpointStore{
idToBreakpoint: make(map[string]*cd.Breakpoint),
pcToBps: make(map[uint64][]*cd.Breakpoint),
bpToPCs: make(map[*cd.Breakpoint][]uint64),
prog: prog,
}
}
// ProcessBreakpointList applies updates received from the Debuglet Controller through a List call.
func (bs *BreakpointStore) ProcessBreakpointList(bps []*cd.Breakpoint) {
bs.mu.Lock()
defer bs.mu.Unlock()
for _, bp := range bps {
if storedBp, ok := bs.idToBreakpoint[bp.Id]; ok {
if storedBp != nil && bp.IsFinalState {
// IsFinalState indicates that the breakpoint has been made inactive.
bs.removeBreakpointLocked(storedBp)
}
} else {
if bp.IsFinalState {
// The controller is notifying us that the breakpoint is no longer active,
// but we didn't know about it anyway.
continue
}
if bp.Action != "" && bp.Action != "CAPTURE" && bp.Action != "LOG" {
bp.IsFinalState = true
bp.Status = &cd.StatusMessage{
Description: &cd.FormatMessage{Format: "Action is not supported"},
IsError: true,
}
bs.errors = append(bs.errors, bp)
// Note in idToBreakpoint that we've already seen this breakpoint, so that we
// don't try to report it as an error multiple times.
bs.idToBreakpoint[bp.Id] = nil
continue
}
pcs, err := bs.prog.BreakpointAtLine(bp.Location.Path, uint64(bp.Location.Line))
if err != nil {
log.Printf("error setting breakpoint at %s:%d: %v", bp.Location.Path, bp.Location.Line, err)
}
if len(pcs) == 0 {
// We can't find a PC for this breakpoint's source line, so don't make it active.
// TODO: we could snap the line to a location where we can break, or report an error to the user.
bs.idToBreakpoint[bp.Id] = nil
} else {
bs.idToBreakpoint[bp.Id] = bp
for _, pc := range pcs {
bs.pcToBps[pc] = append(bs.pcToBps[pc], bp)
}
bs.bpToPCs[bp] = pcs
}
}
}
}
// ErrorBreakpoints returns a slice of Breakpoints that caused errors when the
// BreakpointStore tried to process them, and resets the list of such
// breakpoints.
// The caller is expected to send updates to the server to indicate the errors.
func (bs *BreakpointStore) ErrorBreakpoints() []*cd.Breakpoint {
bs.mu.Lock()
defer bs.mu.Unlock()
bps := bs.errors
bs.errors = nil
return bps
}
// BreakpointsAtPC returns all the breakpoints for which we set a code
// breakpoint at the given address.
func (bs *BreakpointStore) BreakpointsAtPC(pc uint64) []*cd.Breakpoint {
bs.mu.Lock()
defer bs.mu.Unlock()
return bs.pcToBps[pc]
}
// RemoveBreakpoint makes the given breakpoint inactive.
// This is called when either the debugged program hits the breakpoint, or the Debuglet
// Controller informs us that the breakpoint is now inactive.
func (bs *BreakpointStore) RemoveBreakpoint(bp *cd.Breakpoint) {
bs.mu.Lock()
bs.removeBreakpointLocked(bp)
bs.mu.Unlock()
}
func (bs *BreakpointStore) removeBreakpointLocked(bp *cd.Breakpoint) {
// Set the ID's corresponding breakpoint to nil, so that we won't activate it
// if we see it again.
// TODO: we could delete it after a few seconds.
bs.idToBreakpoint[bp.Id] = nil
// Delete bp from the list of cd breakpoints at each of its corresponding
// code breakpoint locations, and delete any code breakpoints which no longer
// have a corresponding cd breakpoint.
var codeBreakpointsToDelete []uint64
for _, pc := range bs.bpToPCs[bp] {
bps := remove(bs.pcToBps[pc], bp)
if len(bps) == 0 {
// bp was the last breakpoint set at this PC, so delete the code breakpoint.
codeBreakpointsToDelete = append(codeBreakpointsToDelete, pc)
delete(bs.pcToBps, pc)
} else {
bs.pcToBps[pc] = bps
}
}
if len(codeBreakpointsToDelete) > 0 {
bs.prog.DeleteBreakpoints(codeBreakpointsToDelete)
}
delete(bs.bpToPCs, bp)
}
// remove updates rs by removing r, then returns rs.
// The mutex in the BreakpointStore which contains rs should be held.
func remove(rs []*cd.Breakpoint, r *cd.Breakpoint) []*cd.Breakpoint {
for i := range rs {
if rs[i] == r {
rs[i] = rs[len(rs)-1]
rs = rs[0 : len(rs)-1]
return rs
}
}
// We shouldn't reach here.
return rs
}
// Copyright 2016 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package breakpoints
import (
"testing"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug"
"cloud.google.com/go/internal/testutil"
cd "google.golang.org/api/clouddebugger/v2"
)
var (
testPC1 uint64 = 0x1234
testPC2 uint64 = 0x5678
testPC3 uint64 = 0x3333
testFile = "foo.go"
testLine uint64 = 42
testLine2 uint64 = 99
testLogPC uint64 = 0x9abc
testLogLine uint64 = 43
testBadPC uint64 = 0xdef0
testBadLine uint64 = 44
testBP = &cd.Breakpoint{
Action: "CAPTURE",
Id: "TestBreakpoint",
IsFinalState: false,
Location: &cd.SourceLocation{Path: testFile, Line: int64(testLine)},
}
testBP2 = &cd.Breakpoint{
Action: "CAPTURE",
Id: "TestBreakpoint2",
IsFinalState: false,
Location: &cd.SourceLocation{Path: testFile, Line: int64(testLine2)},
}
testLogBP = &cd.Breakpoint{
Action: "LOG",
Id: "TestLogBreakpoint",
IsFinalState: false,
Location: &cd.SourceLocation{Path: testFile, Line: int64(testLogLine)},
}
testBadBP = &cd.Breakpoint{
Action: "BEEP",
Id: "TestBadBreakpoint",
IsFinalState: false,
Location: &cd.SourceLocation{Path: testFile, Line: int64(testBadLine)},
}
)
func TestBreakpointStore(t *testing.T) {
p := &Program{breakpointPCs: make(map[uint64]bool)}
bs := NewBreakpointStore(p)
checkPCs := func(expected map[uint64]bool) {
if !testutil.Equal(p.breakpointPCs, expected) {
t.Errorf("got breakpoint map %v want %v", p.breakpointPCs, expected)
}
}
bs.ProcessBreakpointList([]*cd.Breakpoint{testBP, testBP2, testLogBP, testBadBP})
checkPCs(map[uint64]bool{
testPC1: true,
testPC2: true,
testPC3: true,
testLogPC: true,
})
for _, test := range []struct {
pc uint64
expected []*cd.Breakpoint
}{
{testPC1, []*cd.Breakpoint{testBP}},
{testPC2, []*cd.Breakpoint{testBP}},
{testPC3, []*cd.Breakpoint{testBP2}},
{testLogPC, []*cd.Breakpoint{testLogBP}},
} {
if bps := bs.BreakpointsAtPC(test.pc); !testutil.Equal(bps, test.expected) {
t.Errorf("BreakpointsAtPC(%x): got %v want %v", test.pc, bps, test.expected)
}
}
testBP2.IsFinalState = true
bs.ProcessBreakpointList([]*cd.Breakpoint{testBP, testBP2, testLogBP, testBadBP})
checkPCs(map[uint64]bool{
testPC1: true,
testPC2: true,
testPC3: false,
testLogPC: true,
})
bs.RemoveBreakpoint(testBP)
checkPCs(map[uint64]bool{
testPC1: false,
testPC2: false,
testPC3: false,
testLogPC: true,
})
for _, pc := range []uint64{testPC1, testPC2, testPC3} {
if bps := bs.BreakpointsAtPC(pc); len(bps) != 0 {
t.Errorf("BreakpointsAtPC(%x): got %v want []", pc, bps)
}
}
// bs.ErrorBreakpoints should return testBadBP.
errorBps := bs.ErrorBreakpoints()
if len(errorBps) != 1 {
t.Errorf("ErrorBreakpoints: got %d want 1", len(errorBps))
} else {
bp := errorBps[0]
if bp.Id != testBadBP.Id {
t.Errorf("ErrorBreakpoints: got id %q want 1", bp.Id)
}
if bp.Status == nil || !bp.Status.IsError {
t.Errorf("ErrorBreakpoints: got %v, want error", bp.Status)
}
}
// The error should have been removed by the last call to bs.ErrorBreakpoints.
errorBps = bs.ErrorBreakpoints()
if len(errorBps) != 0 {
t.Errorf("ErrorBreakpoints: got %d want 0", len(errorBps))
}
// Even if testBadBP is sent in a new list, it should not be returned again.
bs.ProcessBreakpointList([]*cd.Breakpoint{testBadBP})
errorBps = bs.ErrorBreakpoints()
if len(errorBps) != 0 {
t.Errorf("ErrorBreakpoints: got %d want 0", len(errorBps))
}
}
// Program implements the similarly-named interface in x/debug.
// ValueCollector should only call its BreakpointAtLine and DeleteBreakpoints methods.
type Program struct {
debug.Program
// breakpointPCs contains the state of code breakpoints -- true if the
// breakpoint is currently set, false if it has been deleted.
breakpointPCs map[uint64]bool
}
func (p *Program) BreakpointAtLine(file string, line uint64) ([]uint64, error) {
var pcs []uint64
switch {
case file == testFile && line == testLine:
pcs = []uint64{testPC1, testPC2}
case file == testFile && line == testLine2:
pcs = []uint64{testPC3}
case file == testFile && line == testLogLine:
pcs = []uint64{testLogPC}
default:
pcs = []uint64{0xbad}
}
for _, pc := range pcs {
p.breakpointPCs[pc] = true
}
return pcs, nil
}
func (p *Program) DeleteBreakpoints(pcs []uint64) error {
for _, pc := range pcs {
p.breakpointPCs[pc] = false
}
return nil
}
// Copyright 2016 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package controller is a library for interacting with the Google Cloud Debugger's Debuglet Controller service.
package controller
import (
"context"
"crypto/sha256"
"encoding/json"
"errors"
"fmt"
"log"
"sync"
"golang.org/x/oauth2"
cd "google.golang.org/api/clouddebugger/v2"
"google.golang.org/api/googleapi"
"google.golang.org/api/option"
htransport "google.golang.org/api/transport/http"
)
const (
// agentVersionString identifies the agent to the service.
agentVersionString = "google.com/go-gcp/v0.2"
// initWaitToken is the wait token sent in the first Update request to a server.
initWaitToken = "init"
)
var (
// ErrListUnchanged is returned by List if the server time limit is reached
// before the list of breakpoints changes.
ErrListUnchanged = errors.New("breakpoint list unchanged")
// ErrDebuggeeDisabled is returned by List or Update if the server has disabled
// this Debuggee. The caller can retry later.
ErrDebuggeeDisabled = errors.New("debuglet disabled by server")
)
// Controller manages a connection to the Debuglet Controller service.
type Controller struct {
s serviceInterface
// waitToken is sent with List requests so the server knows which set of
// breakpoints this client has already seen. Each successful List request
// returns a new waitToken to send in the next request.
waitToken string
// verbose determines whether to do some logging
verbose bool
// options, uniquifier and description are used in register.
options Options
uniquifier string
description string
// labels are included when registering the debuggee. They should contain
// the module name, version and minorversion, and are used by the debug UI
// to label the correct version active for debugging.
labels map[string]string
// mu protects debuggeeID
mu sync.Mutex
// debuggeeID is returned from the server on registration, and is passed back
// to the server in List and Update requests.
debuggeeID string
}
// Options controls how the Debuglet Controller client identifies itself to the server.
// See https://cloud.google.com/storage/docs/projects and
// https://cloud.google.com/tools/cloud-debugger/setting-up-on-compute-engine
// for further documentation of these parameters.
type Options struct {
ProjectNumber string // GCP Project Number.
ProjectID string // GCP Project ID.
AppModule string // Module name for the debugged program.
AppVersion string // Version number for this module.
SourceContexts []*cd.SourceContext // Description of source.
Verbose bool
TokenSource oauth2.TokenSource // Source of Credentials used for Stackdriver Debugger.
}
type serviceInterface interface {
Register(ctx context.Context, req *cd.RegisterDebuggeeRequest) (*cd.RegisterDebuggeeResponse, error)
Update(ctx context.Context, debuggeeID, breakpointID string, req *cd.UpdateActiveBreakpointRequest) (*cd.UpdateActiveBreakpointResponse, error)
List(ctx context.Context, debuggeeID, waitToken string) (*cd.ListActiveBreakpointsResponse, error)
}
var newService = func(ctx context.Context, tokenSource oauth2.TokenSource) (serviceInterface, error) {
httpClient, endpoint, err := htransport.NewClient(ctx, option.WithTokenSource(tokenSource))
if err != nil {
return nil, err
}
s, err := cd.New(httpClient)
if err != nil {
return nil, err
}
if endpoint != "" {
s.BasePath = endpoint
}
return &service{s: s}, nil
}
type service struct {
s *cd.Service
}
func (s service) Register(ctx context.Context, req *cd.RegisterDebuggeeRequest) (*cd.RegisterDebuggeeResponse, error) {
call := cd.NewControllerDebuggeesService(s.s).Register(req)
return call.Context(ctx).Do()
}
func (s service) Update(ctx context.Context, debuggeeID, breakpointID string, req *cd.UpdateActiveBreakpointRequest) (*cd.UpdateActiveBreakpointResponse, error) {
call := cd.NewControllerDebuggeesBreakpointsService(s.s).Update(debuggeeID, breakpointID, req)
return call.Context(ctx).Do()
}
func (s service) List(ctx context.Context, debuggeeID, waitToken string) (*cd.ListActiveBreakpointsResponse, error) {
call := cd.NewControllerDebuggeesBreakpointsService(s.s).List(debuggeeID)
call.WaitToken(waitToken)
return call.Context(ctx).Do()
}
// NewController connects to the Debuglet Controller server using the given options,
// and returns a Controller for that connection.
// Google Application Default Credentials are used to connect to the Debuglet Controller;
// see https://developers.google.com/identity/protocols/application-default-credentials
func NewController(ctx context.Context, o Options) (*Controller, error) {
// We build a JSON encoding of o.SourceContexts so we can hash it.
scJSON, err := json.Marshal(o.SourceContexts)
if err != nil {
scJSON = nil
o.SourceContexts = nil
}
const minorversion = "107157" // any arbitrary numeric string
// Compute a uniquifier string by hashing the project number, app module name,
// app module version, debuglet version, and source context.
// The choice of hash function is arbitrary.
h := sha256.Sum256([]byte(fmt.Sprintf("%d %s %d %s %d %s %d %s %d %s %d %s",
len(o.ProjectNumber), o.ProjectNumber,
len(o.AppModule), o.AppModule,
len(o.AppVersion), o.AppVersion,
len(agentVersionString), agentVersionString,
len(scJSON), scJSON,
len(minorversion), minorversion)))
uniquifier := fmt.Sprintf("%X", h[0:16]) // 32 hex characters
description := o.ProjectID
if o.AppModule != "" {
description += "-" + o.AppModule
}
if o.AppVersion != "" {
description += "-" + o.AppVersion
}
s, err := newService(ctx, o.TokenSource)
if err != nil {
return nil, err
}
// Construct client.
c := &Controller{
s: s,
waitToken: initWaitToken,
verbose: o.Verbose,
options: o,
uniquifier: uniquifier,
description: description,
labels: map[string]string{
"module": o.AppModule,
"version": o.AppVersion,
"minorversion": minorversion,
},
}
return c, nil
}
func (c *Controller) getDebuggeeID(ctx context.Context) (string, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.debuggeeID != "" {
return c.debuggeeID, nil
}
// The debuglet hasn't been registered yet, or it is disabled and we should try registering again.
if err := c.register(ctx); err != nil {
return "", err
}
return c.debuggeeID, nil
}
// List retrieves the current list of breakpoints from the server.
// If the set of breakpoints on the server is the same as the one returned in
// the previous call to List, the server can delay responding until it changes,
// and return an error instead if no change occurs before a time limit the
// server sets. List can't be called concurrently with itself.
func (c *Controller) List(ctx context.Context) (*cd.ListActiveBreakpointsResponse, error) {
id, err := c.getDebuggeeID(ctx)
if err != nil {
return nil, err
}
resp, err := c.s.List(ctx, id, c.waitToken)
if err != nil {
if isAbortedError(err) {
return nil, ErrListUnchanged
}
// For other errors, the protocol requires that we attempt to re-register.
c.mu.Lock()
defer c.mu.Unlock()
if regError := c.register(ctx); regError != nil {
return nil, regError
}
return nil, err
}
if resp == nil {
return nil, errors.New("no response")
}
if c.verbose {
log.Printf("List response: %v", resp)
}
c.waitToken = resp.NextWaitToken
return resp, nil
}
// isAbortedError tests if err is a *googleapi.Error, that it contains one error
// in Errors, and that that error's Reason is "aborted".
func isAbortedError(err error) bool {
e, _ := err.(*googleapi.Error)
if e == nil {
return false
}
if len(e.Errors) != 1 {
return false
}
return e.Errors[0].Reason == "aborted"
}
// Update reports information to the server about a breakpoint that was hit.
// Update can be called concurrently with List and Update.
func (c *Controller) Update(ctx context.Context, breakpointID string, bp *cd.Breakpoint) error {
req := &cd.UpdateActiveBreakpointRequest{Breakpoint: bp}
if c.verbose {
log.Printf("sending update for %s: %v", breakpointID, req)
}
id, err := c.getDebuggeeID(ctx)
if err != nil {
return err
}
_, err = c.s.Update(ctx, id, breakpointID, req)
return err
}
// register calls the Debuglet Controller Register method, and sets c.debuggeeID.
// c.mu should be locked while calling this function. List and Update can't
// make progress until it returns.
func (c *Controller) register(ctx context.Context) error {
req := cd.RegisterDebuggeeRequest{
Debuggee: &cd.Debuggee{
AgentVersion: agentVersionString,
Description: c.description,
Project: c.options.ProjectNumber,
SourceContexts: c.options.SourceContexts,
Uniquifier: c.uniquifier,
Labels: c.labels,
},
}
resp, err := c.s.Register(ctx, &req)
if err != nil {
return err
}
if resp == nil {
return errors.New("register: no response")
}
if resp.Debuggee.IsDisabled {
// Setting c.debuggeeID to empty makes sure future List and Update calls
// will call register first.
c.debuggeeID = ""
} else {
c.debuggeeID = resp.Debuggee.Id
}
if c.debuggeeID == "" {
return ErrDebuggeeDisabled
}
return nil
}
// Copyright 2016 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controller
import (
"bytes"
"context"
"errors"
"fmt"
"strconv"
"testing"
"golang.org/x/oauth2"
cd "google.golang.org/api/clouddebugger/v2"
"google.golang.org/api/googleapi"
)
const (
testDebuggeeID = "d12345"
testBreakpointID = "bp12345"
)
var (
// The sequence of wait tokens in List requests and responses.
expectedWaitToken = []string{"init", "token1", "token2", "token1", "token1"}
// The set of breakpoints returned from each List call.
expectedBreakpoints = [][]*cd.Breakpoint{
nil,
{
&cd.Breakpoint{
Id: testBreakpointID,
IsFinalState: false,
Location: &cd.SourceLocation{Line: 42, Path: "foo.go"},
},
},
nil,
}
abortedError error = &googleapi.Error{
Code: 409,
Message: "Conflict",
Body: `{
"error": {
"errors": [
{
"domain": "global",
"reason": "aborted",
"message": "Conflict"
}
],
"code": 409,
"message": "Conflict"
}
}`,
Errors: []googleapi.ErrorItem{
{Reason: "aborted", Message: "Conflict"},
},
}
backendError error = &googleapi.Error{
Code: 503,
Message: "Backend Error",
Body: `{
"error": {
"errors": [
{
"domain": "global",
"reason": "backendError",
"message": "Backend Error"
}
],
"code": 503,
"message": "Backend Error"
}
}`,
Errors: []googleapi.ErrorItem{
{Reason: "backendError", Message: "Backend Error"},
},
}
)
type mockService struct {
t *testing.T
listCallsSeen int
registerCallsSeen int
}
func (s *mockService) Register(ctx context.Context, req *cd.RegisterDebuggeeRequest) (*cd.RegisterDebuggeeResponse, error) {
s.registerCallsSeen++
if req.Debuggee == nil {
s.t.Errorf("missing debuggee")
return nil, nil
}
if req.Debuggee.AgentVersion == "" {
s.t.Errorf("missing agent version")
}
if req.Debuggee.Description == "" {
s.t.Errorf("missing debuglet description")
}
if req.Debuggee.Project == "" {
s.t.Errorf("missing project id")
}
if req.Debuggee.Uniquifier == "" {
s.t.Errorf("missing uniquifier")
}
return &cd.RegisterDebuggeeResponse{
Debuggee: &cd.Debuggee{Id: testDebuggeeID},
}, nil
}
func (s *mockService) Update(ctx context.Context, id, breakpointID string, req *cd.UpdateActiveBreakpointRequest) (*cd.UpdateActiveBreakpointResponse, error) {
if id != testDebuggeeID {
s.t.Errorf("got debuggee ID %s want %s", id, testDebuggeeID)
}
if breakpointID != testBreakpointID {
s.t.Errorf("got breakpoint ID %s want %s", breakpointID, testBreakpointID)
}
if !req.Breakpoint.IsFinalState {
s.t.Errorf("got IsFinalState = false, want true")
}
return nil, nil
}
func (s *mockService) List(ctx context.Context, id, waitToken string) (*cd.ListActiveBreakpointsResponse, error) {
if id != testDebuggeeID {
s.t.Errorf("got debuggee ID %s want %s", id, testDebuggeeID)
}
if waitToken != expectedWaitToken[s.listCallsSeen] {
s.t.Errorf("got wait token %s want %s", waitToken, expectedWaitToken[s.listCallsSeen])
}
s.listCallsSeen++
if s.listCallsSeen == 4 {
return nil, backendError
}
if s.listCallsSeen == 5 {
return nil, abortedError
}
resp := &cd.ListActiveBreakpointsResponse{
Breakpoints: expectedBreakpoints[s.listCallsSeen-1],
NextWaitToken: expectedWaitToken[s.listCallsSeen],
}
return resp, nil
}
func TestDebugletControllerClientLibrary(t *testing.T) {
var (
m *mockService
c *Controller
list *cd.ListActiveBreakpointsResponse
err error
)
m = &mockService{t: t}
newService = func(context.Context, oauth2.TokenSource) (serviceInterface, error) { return m, nil }
opts := Options{
ProjectNumber: "5",
ProjectID: "p1",
AppModule: "mod1",
AppVersion: "v1",
}
ctx := context.Background()
if c, err = NewController(ctx, opts); err != nil {
t.Fatal("Initializing Controller client:", err)
}
if err := validateLabels(c, opts); err != nil {
t.Fatalf("Invalid labels:\n%v", err)
}
if list, err = c.List(ctx); err != nil {
t.Fatal("List:", err)
}
if m.registerCallsSeen != 1 {
t.Errorf("saw %d Register calls, want 1", m.registerCallsSeen)
}
if list, err = c.List(ctx); err != nil {
t.Fatal("List:", err)
}
if len(list.Breakpoints) != 1 {
t.Fatalf("got %d breakpoints, want 1", len(list.Breakpoints))
}
if err = c.Update(ctx, list.Breakpoints[0].Id, &cd.Breakpoint{Id: testBreakpointID, IsFinalState: true}); err != nil {
t.Fatal("Update:", err)
}
if list, err = c.List(ctx); err != nil {
t.Fatal("List:", err)
}
if m.registerCallsSeen != 1 {
t.Errorf("saw %d Register calls, want 1", m.registerCallsSeen)
}
// The next List call produces an error that should cause a Register call.
if list, err = c.List(ctx); err == nil {
t.Fatal("List should have returned an error")
}
if m.registerCallsSeen != 2 {
t.Errorf("saw %d Register calls, want 2", m.registerCallsSeen)
}
// The next List call produces an error that should not cause a Register call.
if list, err = c.List(ctx); err == nil {
t.Fatal("List should have returned an error")
}
if m.registerCallsSeen != 2 {
t.Errorf("saw %d Register calls, want 2", m.registerCallsSeen)
}
if m.listCallsSeen != 5 {
t.Errorf("saw %d list calls, want 5", m.listCallsSeen)
}
}
func validateLabels(c *Controller, o Options) error {
errMsg := new(bytes.Buffer)
if m, ok := c.labels["module"]; ok {
if m != o.AppModule {
errMsg.WriteString(fmt.Sprintf("label module: want %s, got %s\n", o.AppModule, m))
}
} else {
errMsg.WriteString("Missing \"module\" label\n")
}
if v, ok := c.labels["version"]; ok {
if v != o.AppVersion {
errMsg.WriteString(fmt.Sprintf("label version: want %s, got %s\n", o.AppVersion, v))
}
} else {
errMsg.WriteString("Missing \"version\" label\n")
}
if mv, ok := c.labels["minorversion"]; ok {
if _, err := strconv.Atoi(mv); err != nil {
errMsg.WriteString(fmt.Sprintln("label minorversion: not a numeric string:", mv))
}
} else {
errMsg.WriteString("Missing \"minorversion\" label\n")
}
if errMsg.Len() != 0 {
return errors.New(errMsg.String())
}
return nil
}
func TestIsAbortedError(t *testing.T) {
if !isAbortedError(abortedError) {
t.Errorf("isAborted(%+v): got false, want true", abortedError)
}
if isAbortedError(backendError) {
t.Errorf("isAborted(%+v): got true, want false", backendError)
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment