diff --git a/README.md b/README.md index 1ffe6700b5c5..5bd4d49651f2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Google Cloud Client Libraries for Go -[![GoDoc](https://godoc.org/cloud.google.com/go?status.svg)](https://pkg.go.dev/cloud.google.com/go) +[![Go Reference](https://pkg.go.dev/badge/cloud.google.com/go.svg)](https://pkg.go.dev/cloud.google.com/go) Go packages for [Google Cloud Platform](https://cloud.google.com) services. diff --git a/asset/apiv1beta1/asset_client.go b/asset/apiv1beta1/asset_client.go deleted file mode 100644 index 2d3258034159..000000000000 --- a/asset/apiv1beta1/asset_client.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2020 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. - -// Code generated by protoc-gen-go_gapic. DO NOT EDIT. - -package asset - -import ( - "context" - "fmt" - "math" - "net/url" - "time" - - "cloud.google.com/go/longrunning" - lroauto "cloud.google.com/go/longrunning/autogen" - gax "github.com/googleapis/gax-go/v2" - "google.golang.org/api/option" - "google.golang.org/api/option/internaloption" - gtransport "google.golang.org/api/transport/grpc" - assetpb "google.golang.org/genproto/googleapis/cloud/asset/v1beta1" - longrunningpb "google.golang.org/genproto/googleapis/longrunning" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" -) - -var newClientHook clientHook - -// CallOptions contains the retry settings for each method of Client. -type CallOptions struct { - ExportAssets []gax.CallOption - BatchGetAssetsHistory []gax.CallOption -} - -func defaultClientOptions() []option.ClientOption { - return []option.ClientOption{ - internaloption.WithDefaultEndpoint("cloudasset.googleapis.com:443"), - internaloption.WithDefaultMTLSEndpoint("cloudasset.mtls.googleapis.com:443"), - internaloption.WithDefaultAudience("https://cloudasset.googleapis.com/"), - internaloption.WithDefaultScopes(DefaultAuthScopes()...), - option.WithGRPCDialOption(grpc.WithDisableServiceConfig()), - option.WithGRPCDialOption(grpc.WithDefaultCallOptions( - grpc.MaxCallRecvMsgSize(math.MaxInt32))), - } -} - -func defaultCallOptions() *CallOptions { - return &CallOptions{ - ExportAssets: []gax.CallOption{}, - BatchGetAssetsHistory: []gax.CallOption{ - 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.30, - }) - }), - }, - } -} - -// Client is a client for interacting with Cloud Asset API. -// -// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. -type Client struct { - // Connection pool of gRPC connections to the service. - connPool gtransport.ConnPool - - // flag to opt out of default deadlines via GOOGLE_API_GO_EXPERIMENTAL_DISABLE_DEFAULT_DEADLINE - disableDeadlines bool - - // The gRPC API client. - client assetpb.AssetServiceClient - - // LROClient is used internally to handle longrunning operations. - // It is exposed so that its CallOptions can be modified if required. - // Users should not Close this client. - LROClient *lroauto.OperationsClient - - // 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 asset service client. -// -// Asset service definition. -func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) { - clientOpts := defaultClientOptions() - - if newClientHook != nil { - hookOpts, err := newClientHook(ctx, clientHookParams{}) - if err != nil { - return nil, err - } - clientOpts = append(clientOpts, hookOpts...) - } - - disableDeadlines, err := checkDisableDeadlines() - if err != nil { - return nil, err - } - - connPool, err := gtransport.DialPool(ctx, append(clientOpts, opts...)...) - if err != nil { - return nil, err - } - c := &Client{ - connPool: connPool, - disableDeadlines: disableDeadlines, - CallOptions: defaultCallOptions(), - - client: assetpb.NewAssetServiceClient(connPool), - } - c.setGoogleClientInfo() - - c.LROClient, err = lroauto.NewOperationsClient(ctx, gtransport.WithConnPool(connPool)) - if err != nil { - // This error "should not happen", since we are just reusing old connection pool - // and never actually need to dial. - // If this does happen, we could leak connp. However, we cannot close conn: - // If the user invoked the constructor with option.WithGRPCConn, - // we would close a connection that's still in use. - // TODO: investigate error conditions. - return nil, err - } - return c, nil -} - -// Connection returns a connection to the API service. -// -// Deprecated. -func (c *Client) Connection() *grpc.ClientConn { - return c.connPool.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.connPool.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...)) -} - -// ExportAssets exports assets with time and resource types to a given Cloud Storage -// location. The output format is newline-delimited JSON. -// This API implements the -// google.longrunning.Operation API allowing -// you to keep track of the export. -func (c *Client) ExportAssets(ctx context.Context, req *assetpb.ExportAssetsRequest, opts ...gax.CallOption) (*ExportAssetsOperation, error) { - if _, ok := ctx.Deadline(); !ok && !c.disableDeadlines { - cctx, cancel := context.WithTimeout(ctx, 60000*time.Millisecond) - defer cancel() - ctx = cctx - } - md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent()))) - ctx = insertMetadata(ctx, c.xGoogMetadata, md) - opts = append(c.CallOptions.ExportAssets[0:len(c.CallOptions.ExportAssets):len(c.CallOptions.ExportAssets)], opts...) - var resp *longrunningpb.Operation - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.client.ExportAssets(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return &ExportAssetsOperation{ - lro: longrunning.InternalNewOperation(c.LROClient, resp), - }, nil -} - -// BatchGetAssetsHistory batch gets the update history of assets that overlap a time window. -// For RESOURCE content, this API outputs history with asset in both -// non-delete or deleted status. -// For IAM_POLICY content, this API outputs history when the asset and its -// attached IAM POLICY both exist. This can create gaps in the output history. -// If a specified asset does not exist, this API returns an INVALID_ARGUMENT -// error. -func (c *Client) BatchGetAssetsHistory(ctx context.Context, req *assetpb.BatchGetAssetsHistoryRequest, opts ...gax.CallOption) (*assetpb.BatchGetAssetsHistoryResponse, error) { - if _, ok := ctx.Deadline(); !ok && !c.disableDeadlines { - cctx, cancel := context.WithTimeout(ctx, 60000*time.Millisecond) - defer cancel() - ctx = cctx - } - md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent()))) - ctx = insertMetadata(ctx, c.xGoogMetadata, md) - opts = append(c.CallOptions.BatchGetAssetsHistory[0:len(c.CallOptions.BatchGetAssetsHistory):len(c.CallOptions.BatchGetAssetsHistory)], opts...) - var resp *assetpb.BatchGetAssetsHistoryResponse - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.client.BatchGetAssetsHistory(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// ExportAssetsOperation manages a long-running operation from ExportAssets. -type ExportAssetsOperation struct { - lro *longrunning.Operation -} - -// ExportAssetsOperation returns a new ExportAssetsOperation from a given name. -// The name must be that of a previously created ExportAssetsOperation, possibly from a different process. -func (c *Client) ExportAssetsOperation(name string) *ExportAssetsOperation { - return &ExportAssetsOperation{ - lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}), - } -} - -// Wait blocks until the long-running operation is completed, returning the response and any errors encountered. -// -// See documentation of Poll for error-handling information. -func (op *ExportAssetsOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*assetpb.ExportAssetsResponse, error) { - var resp assetpb.ExportAssetsResponse - if err := op.lro.WaitWithInterval(ctx, &resp, time.Minute, opts...); err != nil { - return nil, err - } - return &resp, nil -} - -// Poll fetches the latest state of the long-running operation. -// -// Poll also fetches the latest metadata, which can be retrieved by Metadata. -// -// If Poll fails, the error is returned and op is unmodified. If Poll succeeds and -// the operation has completed with failure, the error is returned and op.Done will return true. -// If Poll succeeds and the operation has completed successfully, -// op.Done will return true, and the response of the operation is returned. -// If Poll succeeds and the operation has not completed, the returned response and error are both nil. -func (op *ExportAssetsOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*assetpb.ExportAssetsResponse, error) { - var resp assetpb.ExportAssetsResponse - if err := op.lro.Poll(ctx, &resp, opts...); err != nil { - return nil, err - } - if !op.Done() { - return nil, nil - } - return &resp, nil -} - -// Metadata returns metadata associated with the long-running operation. -// Metadata itself does not contact the server, but Poll does. -// To get the latest metadata, call this method after a successful call to Poll. -// If the metadata is not available, the returned metadata and error are both nil. -func (op *ExportAssetsOperation) Metadata() (*assetpb.ExportAssetsRequest, error) { - var meta assetpb.ExportAssetsRequest - if err := op.lro.Metadata(&meta); err == longrunning.ErrNoMetadata { - return nil, nil - } else if err != nil { - return nil, err - } - return &meta, nil -} - -// Done reports whether the long-running operation has completed. -func (op *ExportAssetsOperation) Done() bool { - return op.lro.Done() -} - -// Name returns the name of the long-running operation. -// The name is assigned by the server and is unique within the service from which the operation is created. -func (op *ExportAssetsOperation) Name() string { - return op.lro.Name() -} diff --git a/asset/apiv1beta1/asset_client_example_test.go b/asset/apiv1beta1/asset_client_example_test.go deleted file mode 100644 index 45daff4c5b8a..000000000000 --- a/asset/apiv1beta1/asset_client_example_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2020 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. - -// Code generated by protoc-gen-go_gapic. DO NOT EDIT. - -package asset_test - -import ( - "context" - - asset "cloud.google.com/go/asset/apiv1beta1" - assetpb "google.golang.org/genproto/googleapis/cloud/asset/v1beta1" -) - -func ExampleNewClient() { - ctx := context.Background() - c, err := asset.NewClient(ctx) - if err != nil { - // TODO: Handle error. - } - // TODO: Use client. - _ = c -} - -func ExampleClient_ExportAssets() { - // import assetpb "google.golang.org/genproto/googleapis/cloud/asset/v1beta1" - - ctx := context.Background() - c, err := asset.NewClient(ctx) - if err != nil { - // TODO: Handle error. - } - - req := &assetpb.ExportAssetsRequest{ - // TODO: Fill request struct fields. - } - op, err := c.ExportAssets(ctx, req) - if err != nil { - // TODO: Handle error. - } - - resp, err := op.Wait(ctx) - if err != nil { - // TODO: Handle error. - } - // TODO: Use resp. - _ = resp -} - -func ExampleClient_BatchGetAssetsHistory() { - // import assetpb "google.golang.org/genproto/googleapis/cloud/asset/v1beta1" - - ctx := context.Background() - c, err := asset.NewClient(ctx) - if err != nil { - // TODO: Handle error. - } - - req := &assetpb.BatchGetAssetsHistoryRequest{ - // TODO: Fill request struct fields. - } - resp, err := c.BatchGetAssetsHistory(ctx, req) - if err != nil { - // TODO: Handle error. - } - // TODO: Use resp. - _ = resp -} diff --git a/asset/apiv1beta1/doc.go b/asset/apiv1beta1/doc.go deleted file mode 100644 index e117d1eed36d..000000000000 --- a/asset/apiv1beta1/doc.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2020 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. - -// Code generated by protoc-gen-go_gapic. DO NOT EDIT. - -// Package asset is an auto-generated package for the -// Cloud Asset API. -// -// The cloud asset API manages the history and inventory of cloud resources. -// -// Use of Context -// -// The ctx passed to NewClient is used for authentication requests and -// for creating the underlying connection, but is not used for subsequent calls. -// Individual methods on the client use the ctx given to them. -// -// To close the open connection, use the Close() method. -// -// For information about setting deadlines, reusing contexts, and more -// please visit pkg.go.dev/cloud.google.com/go. -package asset // import "cloud.google.com/go/asset/apiv1beta1" - -import ( - "context" - "os" - "runtime" - "strconv" - "strings" - "unicode" - - "google.golang.org/api/option" - "google.golang.org/grpc/metadata" -) - -// For more information on implementing a client constructor hook, see -// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors. -type clientHookParams struct{} -type clientHook func(context.Context, clientHookParams) ([]option.ClientOption, error) - -const versionClient = "20201211" - -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) -} - -func checkDisableDeadlines() (bool, error) { - raw, ok := os.LookupEnv("GOOGLE_API_GO_EXPERIMENTAL_DISABLE_DEFAULT_DEADLINE") - if !ok { - return false, nil - } - - b, err := strconv.ParseBool(raw) - return b, err -} - -// 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.ContainsRune("0123456789.", r) - } - - 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" -} diff --git a/asset/apiv1beta1/mock_test.go b/asset/apiv1beta1/mock_test.go deleted file mode 100644 index 4e3d4796d56a..000000000000 --- a/asset/apiv1beta1/mock_test.go +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2019 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. - -// Code generated by gapic-generator. DO NOT EDIT. - -package asset - -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" - assetpb "google.golang.org/genproto/googleapis/cloud/asset/v1beta1" - longrunningpb "google.golang.org/genproto/googleapis/longrunning" - - 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 mockAssetServer struct { - // Embed for forward compatibility. - // Tests will keep working if more methods are added - // in the future. - assetpb.AssetServiceServer - - reqs []proto.Message - - // If set, all calls return this error. - err error - - // responses to return if err == nil - resps []proto.Message -} - -func (s *mockAssetServer) ExportAssets(ctx context.Context, req *assetpb.ExportAssetsRequest) (*longrunningpb.Operation, 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].(*longrunningpb.Operation), nil -} - -func (s *mockAssetServer) BatchGetAssetsHistory(ctx context.Context, req *assetpb.BatchGetAssetsHistoryRequest) (*assetpb.BatchGetAssetsHistoryResponse, 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].(*assetpb.BatchGetAssetsHistoryResponse), nil -} - -// clientOpt is the option tests should use to connect to the test server. -// It is initialized by TestMain. -var clientOpt option.ClientOption - -var ( - mockAsset mockAssetServer -) - -func TestMain(m *testing.M) { - flag.Parse() - - serv := grpc.NewServer() - assetpb.RegisterAssetServiceServer(serv, &mockAsset) - - 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 TestAssetServiceExportAssets(t *testing.T) { - var expectedResponse *assetpb.ExportAssetsResponse = &assetpb.ExportAssetsResponse{} - - mockAsset.err = nil - mockAsset.reqs = nil - - any, err := ptypes.MarshalAny(expectedResponse) - if err != nil { - t.Fatal(err) - } - mockAsset.resps = append(mockAsset.resps[:0], &longrunningpb.Operation{ - Name: "longrunning-test", - Done: true, - Result: &longrunningpb.Operation_Response{Response: any}, - }) - - var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") - var outputConfig *assetpb.OutputConfig = &assetpb.OutputConfig{} - var request = &assetpb.ExportAssetsRequest{ - Parent: formattedParent, - OutputConfig: outputConfig, - } - - c, err := NewClient(context.Background(), clientOpt) - if err != nil { - t.Fatal(err) - } - - respLRO, err := c.ExportAssets(context.Background(), request) - if err != nil { - t.Fatal(err) - } - resp, err := respLRO.Wait(context.Background()) - - if err != nil { - t.Fatal(err) - } - - if want, got := request, mockAsset.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 TestAssetServiceExportAssetsError(t *testing.T) { - errCode := codes.PermissionDenied - mockAsset.err = nil - mockAsset.resps = append(mockAsset.resps[:0], &longrunningpb.Operation{ - Name: "longrunning-test", - Done: true, - Result: &longrunningpb.Operation_Error{ - Error: &status.Status{ - Code: int32(errCode), - Message: "test error", - }, - }, - }) - - var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") - var outputConfig *assetpb.OutputConfig = &assetpb.OutputConfig{} - var request = &assetpb.ExportAssetsRequest{ - Parent: formattedParent, - OutputConfig: outputConfig, - } - - c, err := NewClient(context.Background(), clientOpt) - if err != nil { - t.Fatal(err) - } - - respLRO, err := c.ExportAssets(context.Background(), request) - if err != nil { - t.Fatal(err) - } - resp, err := respLRO.Wait(context.Background()) - - 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 TestAssetServiceBatchGetAssetsHistory(t *testing.T) { - var expectedResponse *assetpb.BatchGetAssetsHistoryResponse = &assetpb.BatchGetAssetsHistoryResponse{} - - mockAsset.err = nil - mockAsset.reqs = nil - - mockAsset.resps = append(mockAsset.resps[:0], expectedResponse) - - var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") - var contentType assetpb.ContentType = assetpb.ContentType_CONTENT_TYPE_UNSPECIFIED - var readTimeWindow *assetpb.TimeWindow = &assetpb.TimeWindow{} - var request = &assetpb.BatchGetAssetsHistoryRequest{ - Parent: formattedParent, - ContentType: contentType, - ReadTimeWindow: readTimeWindow, - } - - c, err := NewClient(context.Background(), clientOpt) - if err != nil { - t.Fatal(err) - } - - resp, err := c.BatchGetAssetsHistory(context.Background(), request) - - if err != nil { - t.Fatal(err) - } - - if want, got := request, mockAsset.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 TestAssetServiceBatchGetAssetsHistoryError(t *testing.T) { - errCode := codes.PermissionDenied - mockAsset.err = gstatus.Error(errCode, "test error") - - var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]") - var contentType assetpb.ContentType = assetpb.ContentType_CONTENT_TYPE_UNSPECIFIED - var readTimeWindow *assetpb.TimeWindow = &assetpb.TimeWindow{} - var request = &assetpb.BatchGetAssetsHistoryRequest{ - Parent: formattedParent, - ContentType: contentType, - ReadTimeWindow: readTimeWindow, - } - - c, err := NewClient(context.Background(), clientOpt) - if err != nil { - t.Fatal(err) - } - - resp, err := c.BatchGetAssetsHistory(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 -} diff --git a/bigtable/doc.go b/bigtable/doc.go index 37d06c035cc2..f21ac5bb2295 100644 --- a/bigtable/doc.go +++ b/bigtable/doc.go @@ -117,8 +117,8 @@ const ( ) // clientUserAgent identifies the version of this package. -// It should be bumped upon significant changes only. -const clientUserAgent = "cbt-go/20180601" +// It should be the same as https://pkg.go.dev/cloud.google.com/go/bigtable. +const clientUserAgent = "cbt-go/v1.6.0" // resourcePrefixHeader is the name of the metadata header used to indicate // the resource being operated on. diff --git a/datastore/load_test.go b/datastore/load_test.go index b201f08ce2fd..518fa61c10ba 100644 --- a/datastore/load_test.go +++ b/datastore/load_test.go @@ -439,43 +439,6 @@ type withUntypedInterface struct { Field interface{} } -func TestLoadCivilTimeInNonUTCZone(t *testing.T) { - t.Skip("https://github.com/googleapis/google-cloud-go/issues/3402") - src := &pb.Entity{ - Key: keyToProto(testKey0), - Properties: map[string]*pb.Value{ - "Time": {ValueType: &pb.Value_TimestampValue{TimestampValue: ×tamppb.Timestamp{Seconds: 1605504600}}}, - }, - } - dst := &struct{ Time civil.Time }{ - Time: civil.Time{}, - } - want := &struct{ Time civil.Time }{ - Time: civil.Time{ - Hour: 5, - Minute: 30, - }, - } - loc, err := time.LoadLocation("Africa/Cairo") - if err != nil { - t.Fatalf("LoadLocation: %v", err) - } - time.Local = loc - - err = loadEntityProto(dst, src) - if err != nil { - t.Fatalf("loadEntityProto: %v", err) - } - if diff := testutil.Diff(dst, want); diff != "" { - t.Fatalf("Mismatch: got - want +\n%s", diff) - } - loc, err = time.LoadLocation("UTC") - if err != nil { - t.Fatalf("LoadLocation: %v", err) - } - time.Local = loc -} - func TestLoadToInterface(t *testing.T) { testCases := []struct { name string diff --git a/internal/gapicgen/generator/config.go b/internal/gapicgen/generator/config.go index 62c43c2c9272..fdc58fa82a68 100644 --- a/internal/gapicgen/generator/config.go +++ b/internal/gapicgen/generator/config.go @@ -616,14 +616,6 @@ var microgenGapicConfigs = []*microgenConfig{ apiServiceConfigPath: "google/cloud/videointelligence/videointelligence_v1beta2.yaml", releaseLevel: "beta", }, - { - inputDirectoryPath: "google/cloud/asset/v1beta1", - pkg: "asset", - importPath: "cloud.google.com/go/asset/apiv1beta1", - gRPCServiceConfigPath: "google/cloud/asset/v1beta1/cloudasset_grpc_service_config.json", - apiServiceConfigPath: "google/cloud/asset/v1beta1/cloudasset_v1beta1.yaml", - releaseLevel: "beta", - }, { inputDirectoryPath: "google/cloud/asset/v1p2beta1", pkg: "asset", diff --git a/pubsublite/internal/wire/publish_batcher.go b/pubsublite/internal/wire/publish_batcher.go index 8a32accf248f..a6d150565f24 100644 --- a/pubsublite/internal/wire/publish_batcher.go +++ b/pubsublite/internal/wire/publish_batcher.go @@ -18,7 +18,7 @@ import ( "errors" "fmt" - "cloud.google.com/go/pubsublite/common" + "cloud.google.com/go/pubsublite/publish" "google.golang.org/api/support/bundler" "google.golang.org/protobuf/proto" @@ -28,7 +28,7 @@ import ( var errPublishQueueEmpty = errors.New("pubsublite: received publish response from server with no batches in flight") // PublishResultFunc receives the result of a publish. -type PublishResultFunc func(*common.PublishMetadata, error) +type PublishResultFunc func(*publish.Metadata, error) // messageHolder stores a message to be published, with associated metadata. type messageHolder struct { @@ -141,7 +141,7 @@ func (b *publishMessageBatcher) OnPublishResponse(firstOffset int64) error { batch, _ := frontElem.Value.(*publishBatch) for i, msgHolder := range batch.msgHolders { // Messages are ordered, so the offset of each message is firstOffset + i. - pm := &common.PublishMetadata{Partition: b.partition, Offset: firstOffset + int64(i)} + pm := &publish.Metadata{Partition: b.partition, Offset: firstOffset + int64(i)} msgHolder.onResult(pm, nil) b.availableBufferBytes += msgHolder.size } diff --git a/pubsublite/internal/wire/publish_batcher_test.go b/pubsublite/internal/wire/publish_batcher_test.go index 3e12fe2bef17..b36fed6fe75a 100644 --- a/pubsublite/internal/wire/publish_batcher_test.go +++ b/pubsublite/internal/wire/publish_batcher_test.go @@ -19,8 +19,8 @@ import ( "time" "cloud.google.com/go/internal/testutil" - "cloud.google.com/go/pubsublite/common" "cloud.google.com/go/pubsublite/internal/test" + "cloud.google.com/go/pubsublite/publish" "github.com/google/go-cmp/cmp" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -35,7 +35,7 @@ type testPublishResultReceiver struct { done chan struct{} msg string t *testing.T - got *common.PublishMetadata + got *publish.Metadata gotErr error } @@ -47,7 +47,7 @@ func newTestPublishResultReceiver(t *testing.T, msg *pb.PubSubMessage) *testPubl } } -func (r *testPublishResultReceiver) set(pm *common.PublishMetadata, err error) { +func (r *testPublishResultReceiver) set(pm *publish.Metadata, err error) { r.got = pm r.gotErr = err close(r.done) diff --git a/pubsublite/common/publish_data.go b/pubsublite/publish/metadata.go similarity index 57% rename from pubsublite/common/publish_data.go rename to pubsublite/publish/metadata.go index a4658160d18a..5a3c4a4b4d05 100644 --- a/pubsublite/common/publish_data.go +++ b/pubsublite/publish/metadata.go @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and -package common +package publish import ( "fmt" @@ -19,28 +19,28 @@ import ( "strings" ) -// PublishMetadata holds the results of a published message. -type PublishMetadata struct { +// Metadata holds the results of a published message. +type Metadata struct { Partition int Offset int64 } -func (pm *PublishMetadata) String() string { - return fmt.Sprintf("%d:%d", pm.Partition, pm.Offset) +func (m *Metadata) String() string { + return fmt.Sprintf("%d:%d", m.Partition, m.Offset) } -// ParsePublishMetadata converts a string obtained from PublishMetadata.String() -// back to PublishMetadata. -func ParsePublishMetadata(input string) (*PublishMetadata, error) { +// ParseMetadata converts a string obtained from Metadata.String() +// back to Metadata. +func ParseMetadata(input string) (*Metadata, error) { parts := strings.Split(input, ":") if len(parts) != 2 { - return nil, fmt.Errorf("pubsublite: invalid encoded PublishMetadata %q", input) + return nil, fmt.Errorf("pubsublite: invalid encoded publish metadata %q", input) } partition, pErr := strconv.ParseInt(parts[0], 10, 64) offset, oErr := strconv.ParseInt(parts[1], 10, 64) if pErr != nil || oErr != nil { - return nil, fmt.Errorf("pubsublite: invalid encoded PublishMetadata %q", input) + return nil, fmt.Errorf("pubsublite: invalid encoded publish metadata %q", input) } - return &PublishMetadata{Partition: int(partition), Offset: offset}, nil + return &Metadata{Partition: int(partition), Offset: offset}, nil } diff --git a/pubsublite/common/publish_data_test.go b/pubsublite/publish/metadata_test.go similarity index 76% rename from pubsublite/common/publish_data_test.go rename to pubsublite/publish/metadata_test.go index 891513745842..e9529934b733 100644 --- a/pubsublite/common/publish_data_test.go +++ b/pubsublite/publish/metadata_test.go @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and -package common +package publish import ( "testing" @@ -23,18 +23,18 @@ func TestPublishMetadataStringEncoding(t *testing.T) { for _, tc := range []struct { desc string input string - want *PublishMetadata + want *Metadata wantErr bool }{ { desc: "valid: zero", input: "0:0", - want: &PublishMetadata{Partition: 0, Offset: 0}, + want: &Metadata{Partition: 0, Offset: 0}, }, { desc: "valid: non-zero", input: "3:1234", - want: &PublishMetadata{Partition: 3, Offset: 1234}, + want: &Metadata{Partition: 3, Offset: 1234}, }, { desc: "invalid: number", @@ -53,14 +53,14 @@ func TestPublishMetadataStringEncoding(t *testing.T) { }, } { t.Run(tc.desc, func(t *testing.T) { - got, gotErr := ParsePublishMetadata(tc.input) + got, gotErr := ParseMetadata(tc.input) if !testutil.Equal(got, tc.want) || (gotErr != nil) != tc.wantErr { - t.Errorf("ParsePublishMetadata(%q): got (%v, %v), want (%v, err=%v)", tc.input, got, gotErr, tc.want, tc.wantErr) + t.Errorf("ParseMetadata(%q): got (%v, %v), want (%v, err=%v)", tc.input, got, gotErr, tc.want, tc.wantErr) } if tc.want != nil { if got := tc.want.String(); got != tc.input { - t.Errorf("PublishMetadata(%v).String(): got %q, want: %q", tc.want, got, tc.input) + t.Errorf("Metadata(%v).String(): got %q, want: %q", tc.want, got, tc.input) } } }) diff --git a/spanner/client.go b/spanner/client.go index eb5ff9798d36..8f14968e04b4 100644 --- a/spanner/client.go +++ b/spanner/client.go @@ -451,6 +451,11 @@ func (c *Client) rwTransaction(ctx context.Context, f func(context.Context, *Rea var ( sh *sessionHandle ) + defer func() { + if sh != nil { + sh.recycle() + } + }() err = runWithRetryOnAbortedOrSessionNotFound(ctx, func(ctx context.Context) error { var ( err error @@ -480,9 +485,6 @@ func (c *Client) rwTransaction(ctx context.Context, f func(context.Context, *Rea resp, err = t.runInTransaction(ctx, f) return err }) - if sh != nil { - sh.recycle() - } return resp, err } diff --git a/spanner/client_test.go b/spanner/client_test.go index 92cac5564bb8..4fc6219a0f4a 100644 --- a/spanner/client_test.go +++ b/spanner/client_test.go @@ -828,6 +828,34 @@ func TestClient_ReadWriteTransaction_Update_QueryOptions(t *testing.T) { } } +func TestClient_ReadWriteTransaction_DoNotLeakSessionOnPanic(t *testing.T) { + // Make sure that there is always only one session in the pool. + sc := SessionPoolConfig{ + MinOpened: 1, + MaxOpened: 1, + } + _, client, teardown := setupMockedTestServerWithConfig(t, ClientConfig{SessionPoolConfig: sc}) + defer teardown() + ctx := context.Background() + + // If a panic occurs during a transaction, the session will not leak. + func() { + defer func() { recover() }() + + _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error { + panic("cause panic") + return nil + }) + if err != nil { + t.Fatalf("Unexpected error during transaction: %v", err) + } + }() + + if g, w := client.idleSessions.idleList.Len(), 1; g != w { + t.Fatalf("idle session count mismatch.\nGot: %v\nWant: %v", g, w) + } +} + func TestClient_SessionNotFound(t *testing.T) { // Ensure we always have at least one session in the pool. sc := SessionPoolConfig{ diff --git a/spanner/integration_test.go b/spanner/integration_test.go index 47b266d7e753..2881813c533b 100644 --- a/spanner/integration_test.go +++ b/spanner/integration_test.go @@ -52,6 +52,10 @@ var ( // by setting environment variable GCLOUD_TESTS_GOLANG_PROJECT_ID. testProjectID = testutil.ProjID() + // spannerHost specifies the spanner API host used for testing. It can be changed + // by setting the environment variable GCLOUD_TESTS_GOLANG_SPANNER_HOST + spannerHost = getSpannerHost() + dbNameSpace = uid.NewSpace("gotest", &uid.Options{Sep: '_', Short: true}) instanceNameSpace = uid.NewSpace("gotest", &uid.Options{Sep: '-', Short: true}) backupIDSpace = uid.NewSpace("gotest", &uid.Options{Sep: '_', Short: true}) @@ -165,6 +169,10 @@ func parseInstanceName(inst string) (project, instance string, err error) { return matches[1], matches[2], nil } +func getSpannerHost() string { + return os.Getenv("GCLOUD_TESTS_GOLANG_SPANNER_HOST") +} + const ( str1 = "alice" str2 = "a@example.com" @@ -195,6 +203,9 @@ func initIntegrationTests() (cleanup func()) { } opts := grpcHeaderChecker.CallOptions() + if spannerHost != "" { + opts = append(opts, option.WithEndpoint(spannerHost)) + } var err error // Create InstanceAdmin and DatabaseAdmin clients. instanceAdmin, err = instance.NewInstanceAdminClient(ctx, opts...) @@ -3090,7 +3101,11 @@ func isNaN(x interface{}) bool { // createClient creates Cloud Spanner data client. func createClient(ctx context.Context, dbPath string, spc SessionPoolConfig) (client *Client, err error) { - client, err = NewClientWithConfig(ctx, dbPath, ClientConfig{SessionPoolConfig: spc}) + opts := grpcHeaderChecker.CallOptions() + if spannerHost != "" { + opts = append(opts, option.WithEndpoint(spannerHost)) + } + client, err = NewClientWithConfig(ctx, dbPath, ClientConfig{SessionPoolConfig: spc}, opts...) if err != nil { return nil, fmt.Errorf("cannot create data client on DB %v: %v", dbPath, err) }